これは、display_errorsによるエラーメッセージ表示がHTMLエスケープされていないことが原因です。簡単なサンプルを以下に示します。
このスクリプトに、x=<script>alert(1)</script> というクエリ文字列をつけて呼び出すと下記の表示になります(error_reporting設定でE_NOTICEが有効になっている場合です。E_NOTICEはphp.ini上の設定がない場合は無効ですが、多くのphp.ini設定では有効となっています)。<?php ini_set('display_errors', 1); // display_errorsを有効にする $a = array(); // 配列の生成 $index = $_GET['x']; // 配列のインデックスを得る $b = $a[$index]; // 配列の要素にアクセス
この際のHTMLソースは以下の通りです。
Notice: Undefined index: <script>alert(1)</script> in /var/www/de.php on line 5Undefined indexとして、外部から与えたパラメータが「そのまま」(HTMLエスケープされずに)出力されています。
PHPの脆弱性なのか?
これはPHPの脆弱性なのでしょうか。CVE-2008-5814が脆弱性なのであれば、こちらも脆弱性と見るべきだと思います。そうしないと辻褄があいません。一方、display_errorsはデバッグ用の設定なのだから、本番環境に適用する方が悪いという意見もあるでしょう。この立場に立てば、上記はPHPの脆弱性ではないという見方もあり得ます。その場合は、CVE-2008-5814も(実は)脆弱性ではないとしなければなりませんが…
しかし、仮にPHPの脆弱性でないとしても、display_errorsによるエラー表示はHTMLエスケープすべきだと考えます。そうしないと、正しい表示にならないからです。
先の例では、Undefined indexとして表示すべき内容は、<script>alert(1)</script> という文字列であって、JavaScriptの実行ではありません。display_errorsによるエラー表示はHTMLエスケープしなければ、正しい表示にならない場合があります。すなわち、HTMLのエスケープ漏れはPHPの仕様上の不具合と言えます。そして、仕様上の不具合(バグ)が環境依存とはいえ脆弱性になり得るとすれば、結局PHP自体の脆弱性と言わざるを得ないと徳丸は考えます。
プログラミングでなんとかならないのか?
配列の未定義のキーによるアクセスを防ぐこと自体は難しくありません。たとえば、array_key_exists関数を使って、キーの存在を事前に確認する方法。$b = array_key_exists($index, $a) ? $a[$index] : null;同じくisset()を使う方法。
$b = isset($a[$index]) ? $a[$index] : null;エラー制御演算子(@)を使ってエラー表示を抑止する方法。
$b = @$a[$index];すべての箇所で上記を徹底すれば、Undefined indexの警告によるXSSは防げるはずです。しかし、その他のエラー・警告も含め、すべてを漏れなくなくすことは難しいため、display_errorsはやはり無効にすべきです。
display_errorsは本番環境ではOffにしよう
従来から、セキュリティ上の理由からデバッグ目的のエラー表示は抑止すべしと言われています。その主な理由は下記の通りです。
- デバッグ用のエラーメッセージが攻撃者にとってヒントになる場合がある
- エラーメッセージを通じて情報漏洩させる手口がある(→参考)
- そもそもユーザフレンドリでないし、みっともない(セキュリティ上の理由ではない)
- display_errorsなどデバッグ用のエラー表示は本番リリース前にオフにする
- エラー表示は利用者向けに必要最低限の表示にとどめ、問題解決用の情報はログとして出力する
- エラーメッセージにはクロスサイトスクリプティングが入りやすいので注意(と言っても、特別なことをするわけではなく、エラー表示だからと言って手を抜かずにHTMLエスケープしろということです)
0 件のコメント:
コメントを投稿