セキュアな PHP アプリケーションを作成するための 7 つの習慣
PHP アプリケーションのセキュリティーには、リモートでのセキュリティーとローカルでのセキュリティーに関するものがあります。リモートとローカル両方でのセ キュリティーに対応した Web アプリケーションを実装するために、PHP 開発者が身につける必要のある習慣を学びましょう。
セキュリティーを考慮する際には、実際のプラットフォームとオペレーティング・システムのセキュリティーの問題に対処した上で、さらに作成するアプ リケーションをセキュアなものにする必要があるということを忘れてはなりません。PHP アプリケーションを作成する際には、可能な限りセキュアなアプリケーションにするために、次の 7 つの習慣を守る必要があります。
- 入力を検証する
- ファイルシステムを保護する
- データベースを保護する
- セッション・データを保護する
- XSS (Cross-Site Scripting: クロスサイト・スクリプティング) の脆弱性から保護する
- フォームへの投稿を検証する
- CSRF (Cross-Site Request Forgeries: クロスサイト・リクエスト・フォージェリー) から保護する
データの検証は、セキュリティー対策として採ることのできる習慣のなかで最も重要なものです。そして入力に関する限り、その習慣は単純で、ユーザー を信用してはいけないというものです。皆さんのユーザーはおそらく善良であり、ほとんどのユーザーは皆さんのアプリケーションを想定どおり使うでしょう。 しかし入力可能な部分がある限り、非常に悪質な入力をすることもできます。皆さんはアプリケーションの開発者として、悪質な入力からアプリケーションを保 護する必要があります。ユーザー入力が現在どのように処理され、どのような入力が適切なのかを注意深く考えることによって、堅牢でセキュアなアプリケー ションを作成することができます。
ファイルシステムやデータベースとのやり取りは後ほど説明しますが、あらゆる種類の検証をカバーできる、検証のための一般的なヒントがあります。
- ホワイトリスト化された値を使う
- 制限付きの選択肢であっても必ず再検証する
- 組み込みのエスケープ関数を使う
- データ型が (数字など) 適切かどうかを検証する
ホワイトリスト化された値は有効な値であり、ブラックリスト化された無効な値とは逆です。ホワイトリストがブラックリストと異なる点は、検証を行う 際に、無効な値 (多くの場合は未知の値または想定外の値) をリストにするよりも、取り得る値をリストにした方がリストに示される値も少なく、リストのサイズも小さくなることが多い、という点にあります。
検証を行う際には、未知のあらゆる値に対して保護しようとするよりも、アプリケーションが何を許可するのかを概念化し、それを検証した方が容易な場 合が多いということを覚えておくことです。例えばフィールドへの入力値を数字のみに制限する場合には、入力がすべて数字であることを確認するルーチンを作 成します。数字ではない値を検索し、そうした値が見つかったら無効とマークするようなルーチンを作成してはいけません。
2000年7月、ある Web サイトから、ある Web サーバー上のファイルの中にある顧客データが流出してしまいました。その Web サイトの訪問者が、そのデータを含むファイルを見られるように URL を操作したのです。それらのファイルは不適切な場所に置かれていたのですが、この例はファイルシステムを攻撃者から保護することの重要性を物語っていま す。
ファイルシステムに対するファイル操作を含む PHP アプリケーションに、ユーザーから入力される可変データがある場合には、ユーザー入力を十分に検証し、ユーザーがファイルシステムに対して望ましくないこ とを絶対に行えないようにする必要があります。リスト 1 は、指定された名前の画像をダウンロードする PHP サイトの例を示しています。
リスト 1. ファイルのダウンロード
<?php |
これを見るとわかるように、リスト 1 のスクリプトは比較的危険性が高く、Web サーバーが読み取りアクセス可能な任意のファイルを提供してしまいます。例えばセッション・ディレクトリーの中のファイル (「セッションを保護する」を参照) や、さらには /etc/passwd などのシステム・ファイルさえ提供してしまいます。この例にはファイル名を入力できるテキスト・ボックスがありますが、クエリー・ストリングの中に容易にファイル名を入れることもできるのです。
ファイルシステムへのアクセスをユーザー入力と一緒に構成することは危険なため、そうした構成を完全に避けるようにした方が賢明です。そのために
は、データベースと、生成される隠しファイル名を使うようにアプリケーションを設計します。しかし必ずしもそうはいかない場合もあります。リスト 2
はファイル名を検証するルーチンの一例です。この例では、正規表現を使って有効な文字のみがファイル名に使われるようにしており、ドット文字 (.) が続いていないか具体的なチェックをしています。
リスト 2. ファイル名の文字が有効かどうかのチェック
function isValidFileName($file) { |
|
2008年4月、クエリー・ストリングの中に SQL の列の名前が使用されていたことによって、米国のある州の Department of Corrections (刑務局) から機密データが流出してしまいました。この漏洩事件では、悪意のあるユーザーが、表示させたいデータベースの列を選択するようにして、そのページを送信 することで、そのデータを取得することができたのです。この漏洩事件によって、アプリケーション開発者がまったく予期しなかったような入力をユーザーが考 え出して実行する手口が明らかになり、SQL インジェクション攻撃に対して慎重に対策を施す必要があることが改めて示されました。
リスト 3 は SQL 文を実行するスクリプトの一例を示しています。この例の SQL
文は動的であり、上記の攻撃と同じ攻撃を許してしまいます。このフォームの所有者は、自分たちは安全だと考えたくなるかもしれません。列の名前は選択用リ
ストにあるものに制限されているからです。しかしこのコードは、フォーム・スプーフィングに関して一番最後に注意しなければならないことが漏れています。
つまり、たとえコードによって選択肢がドロップダウン・ボックスに制限されているからといって、誰かが何かの悪事 (例えばアスタリスク [*] など) を含むフォームを投稿できないということにはならないのです。
リスト 3. SQL 文を実行する
<html> |
そこで、データベースを保護するための習慣を身につけるには、動的な SQL コードを可能な限り避けるようにすることです。動的な SQL コードを避けられない場合には、SQL の列に直接ユーザー入力を適用してはいけません。リスト 4 は、静的な列を使うことに加えて account number フィールドに数字以外を入力することができなくなるように単純な検証ルーチンを追加することがいかに強力かを示す一例です。
リスト 4. 検証による保護と
mysql_real_escape_string()<html> |
またこの例は mysql_real_escape_string() 関数の使い方も示しています。この関数は入力に無効な文字が含まれないように入力を適切に検証します。magic_quotes_gpc に頼っている方には、PHP V6 ではmagic_quotes_gpc が非推奨となり、削除されることをあらかじめ警告しておきます。magic_quotes_gpc に頼ることを今すぐに止め、magic_quotes_gpc を使わなくてもセキュアであるように PHP アプリケーションを作成する必要があります。また ISP を使用している場合には、その ISP は magic_quotes_gpc を有効にしていない可能性があります。
最後に、改善された例を見ると、SQL 文と出力に動的な列選択が含まれていないことがわかります。こうすることによって、異なる情報を持つ列を後でテーブルに追加する場合には、そうした列を出 力することができます。またデータベースを扱うためにフレームワークを使用している場合には、そのフレームワークが SQL 検証を行ってくれるのかもしれません。そのフレームワークのドキュメントを調べ、そのことを確認してください。それでもよくわからない場合には、十分すぎ るぐらいに安全になるように検証を行う必要があります。データベースとのやり取りにフレームワークを使用している場合であっても、それ以外の検証は相変わ らず必要なのです。
|
デフォルトで、PHP のセッション情報は一時ディレクトリーに書き込まれます。リスト 5 のフォームを考えてみてください。このフォームはユーザーの ID とアカウント番号をセッションに保存する方法を示しています。
リスト 5. データをセッションに保存する
<?php |
リスト 6 は /tmp ディレクトリーの内容を示しています。
リスト 6. /tmp ディレクトリーの中のセッション・ファイル
-rw------- 1 _www wheel 97 Aug 18 20:00 sess_9e4233f2cd7cae35866cd8b61d9fa42b |
セッション・ファイルを出力してみると (リスト 7)、ご覧のように非常に読みやすいフォーマットで情報が含まれています。このファイルは Web サーバーのユーザーが読み書きできる必要があるため、セッション・ファイルは共有サーバーを利用している誰に対しても大きな問題を引き起こす可能性があり ます。これらのファイルを読み取るスクリプトを皆さん以外の誰かが作成し、セッションから値を取得することができてしまいます。
リスト 7. セッション・ファイルの内容
userName|s:5:"ngood";accountNumber|s:9:"123456789"; |
|
セッション・データを保護するためにできることは 2 つあります。第 1 の方法は、セッションの中に入れるものをすべて暗号化する方法です。しかし単にデータを暗号化したからといって、そのデータが完全に安全ということにはな りません。そのため、セッションを保護する唯一の手段としてデータを暗号化する場合には、十分に注意する必要があります。もう 1 つの方法は、セッション・データをデータベースなど別の場所に保管する方法です。この場合も必ずデータベースをロックする必要がありますが、この方法を利 用すると 2 つの問題を解決することができます。第 1 に、共有ファイルシステムよりも安全な場所にデータを置くことができます。そして第 2 に、複数ホストでセッションが共有されるため、アプリケーションを複数の Web サーバーにまたがるスケーラブルなものにすることができます。
独自のセッション・パーシスタンスを実装するためには、PHP の session_set_save_handler()
関数を調べてみてください。この関数を使うと、セッション情報をデータベースに保管したり、あるいはすべてのデータの暗号化と復号を行うハンドラーを実装
したりすることができます。リスト 8 はこの関数の使い方の例と実装用のスケルトン関数を示しています。データベースの使い方の例については「参考文献」で調べることもできます。
リスト 8.
session_set_save_handler() 関数の例function open($save_path, $session_name) |
|
XSS の脆弱性は、Web サイトの脆弱性に関する 2007年の報告全体の大きな部分を占めています (「参考文献」 を参照)。XSS の脆弱性は、ユーザーが Web ページに HTML コードを注入できる場合に起こります。HTML コードではスクリプト・タグの中に JavaScript コードを含めることができるため、ページが描画されると JavaScript を実行できてしまいます。リスト 9 のフォームは、テキストを入力することが一般的な、フォーラムやウィキ、ソーシャル・ネットワーキング、その他すべてのサイトに当てはまります。
リスト 9. テキストを入力するためのフォーム
<html> |
リスト 10 は、このフォームがどのように結果を出力するか、またこのフォームに対して XSS 攻撃が可能なことを示しています。
リスト 10. showResults.php
<html> |
リスト 11 は新しいウィンドウをポップアップして Google のホーム・ページを開く方法の基本的なサンプル・コードです。Web アプリケーションが XSS 攻撃に対して保護されていない場合、被る被害を制限する唯一の方法は、どんな攻撃が可能かを想像してみることです。例えば、サイトのスタイルを真似たリン クをフィッシングの目的で追加することができます (「参考文献」)。
リスト 11. 悪意の入力テキストのサンプル・コード
<script type="text/javascript">myRef = window.open('http://www.google.com','mywin', |
XSS 攻撃から保護するためには、変数の値が出力として表示される場合に必ず htmlentities() 関数によるフィルターに入力を通す必要があります。Web アプリケーションでの名前や E メール・アドレス、電話番号、請求情報などの入力に対して、ホワイトリスト化された値を使って入力データを検証する、という第 1 の習慣に従うことを忘れないでください。
テキスト入力がある、もっと安全なバージョンのページを次に示します。
リスト 12. よりセキュアなフォーム
<html> |
|
フォーム・スプーフィングというのは、誰かがフォームに対して想定外の場所から投稿することです。フォーム・スプーフィングを行う最も簡単な方法 は、フォームにすべての値を送信して渡すような Web ページを作成することです。Web アプリケーションはステートレスであるため、投稿されたデータが想定の場所から来たのかどうかを絶対確実に確認する方法はありません。結局のところ、IP アドレスからホスト名まで、あらゆるものがスプーフィングの対象となりうるのです。リスト 13 は情報を入力するための典型的なフォームです。
リスト 13. テキストを処理するためのフォーム
<html> |
リスト 14 はリスト 13 のフォームに投稿されるフォームです。このフォームを使ってフォーム・スプーフィングを試すためには、このフォームを Web サイトに置き、リスト 14 のコードを HTML 文書としてデスクトップに保存し、保存したフォームをブラウザーで開きます。次にフォームにデータを入力したら、フォームを送信し、データが処理される様 子を観察します。
リスト 14. データを収集するためのフォーム
<html> |
フォーム・スプーフィングによる本当の影響は、ドロップダウン・ボックスやラジオ・ボタン、チェックボックス、その他制限付きの入力を持つフォーム がある場合、そうした制限がフォーム・スプーフィングによってまったく無意味になってしまうことです。リスト 15 のコードについて考えてみてください。このコードには無効なデータを持つフォームが含まれています。
リスト 15. 無効なデータを持つフォーム
<html> |
少し考えてみてください。ユーザーの入力を限られたデータに制限するドロップダウン・ボックスやラジオ・ボタンがあると、入力の検証は必要ないと考 えがちです。しかし結局のところ、その入力フォームは、ある限られたデータのみをユーザーが入力できるように保証しているのです。フォーム・スプーフィン グを制限するためには、投稿者が本人であるらしいということを確実にする手段を講じる必要があります。そのために使用できる 1 つの手法として、1 回しか使用できないトークンを使う方法があります。1 回限りのトークンを使う場合であってもフォーム・スプーフィングは不可能ではありませんが、フォーム・スプーフィングを非常に行いにくくすることができま す。フォームが描画されるたびにトークンが変更されるため、攻撃をしようとする人は、送信されるフォームのインスタンスを取得し、そこから抽出したトーク ンをスプーフィング用のフォームに入れなければなりません。この手法によって、望ましくないリクエストをアプリケーションに投稿するための永久的な Web フォームを誰かが作成できる可能性が非常に低くなります。リスト 16 はこのワンタイム・トークンを埋め込んだフォームの例を示しています。
リスト 16. ワンタイム・トークンを埋め込んだフォームを使う
<?php |
|
クロスサイト・リクエスト・フォージェリー (CSRF 攻撃) は、攻撃を実行できるユーザー特権を悪用します。CSRF 攻撃では、ユーザーが簡単に思いもよらぬ共犯者になることができます。リスト 17 は、あるアクションを実行するページの例を示しています。このページはクッキーからユーザーのログイン情報を調べます。クッキーが有効である限り、この Web ページはリクエストを処理します。
リスト 17. CSRF の例
<img src="http://www.example.com/processSomething?id=123456789" /> |
CSRF 攻撃は多くの場合 <img>
タグの形で行われますが、これはブラウザーが画像を取得しようとして不注意にその画像の URL
を呼び出すからです。しかしその画像のソースは、同じサイト上のページにある、渡されたパラメーターに基づいて何らかの処理を行う URL
であることも十分考えられるのです。XSS 攻撃が潜んだ <img> タグが置かれると (これは報告された攻撃のうち最も一般的なものです)、ユーザーは自分たちのクレデンシャルを使って (気付かないうちに) 容易に何かをすることができ、従って CSRF が行われてしまいます。
CSRF から保護するためには、フォームの投稿を検証するための習慣で使用する、ワンタイム・トークンによる手法を使います。また、$_REQUEST ではなく、明示的な $_POST 変数を使う必要があります。リスト 18 は、お粗末な Web ページの例を示しています。この Web ページは、そのページが GET リクエストによって呼び出されるのか、あるいはそのページにフォームが投稿されたために呼び出されるかにかかわらず、同じように処理してしまいます。
リスト 18.
$_REQUEST からデータを取得する<html> |
リスト 19 はフォームの POST によってのみ動作するように改善したバージョンを示しています。
リスト 19.
$_POST のみからデータを取得する<html> |
|
この記事で紹介した、セキュアな PHP Web アプリケーションを作成するための 7 つの習慣から始めることによって、容易に悪意のある攻撃の犠牲者になる事態を避けることができます。他の多くの習慣と同様、これらの習慣も最初は面倒に思 えるかもしれませんが、時間が経つにつれ自然なことに思えるようになります。
最初の習慣、つまり入力の検証が鍵であることを忘れないでください。入力が不適切な値を含まないことを確認した後、ファイルシステムやデータベー ス、そしてセッションの保護に進むことができます。最後に、PHP コードが XSS 攻撃やフォーム・スプーフィング、CSRF 攻撃に耐えられること確認する必要があります。こうした習慣を注意しながら身につけることが、簡単に攻撃される事態を防ぐ上で非常に効果的なのです。
トラックバック(0)
このブログ記事を参照しているブログ一覧: セキュアな PHP アプリケーションを作成するための 7 つの習慣
このブログ記事に対するトラックバックURL: http://blog.amhp.jp/cms/mt-tb.cgi/150
コメントする