PHPの正しいエラー処理
エラーを無視しがちなPHPで安心ガードする、または「require strict;」 - uzullaがブログという記事を見ていて、エラーが発生した時に必ずエラーを表示する次のようなコードを見かけた。
<?php // strict error bailout function strict_error_handler($errno, $errstr, $errfile, $errline) { die ("STRICT: {$errno} {$errstr} {$errfile} {$errline} ".PHP_EOL); } set_error_handler("strict_error_handler");
これだと、スクリプト内で発生したエラーがdieで本番でも必ず表示される。開発環境でエラーを表示するのはいいが、これでは本番環境でもPHPのエラーが直接表示されてしまう。これは、XSSの可能性を残すのでセキュアではない。元記事だとCLIに限定するということは特に書いてなかったので一応指摘する。
なぜセキュアではないのかは、PHPのdisplay_errorsが有効だとカジュアルにXSS脆弱性が入り込む | 徳丸浩の日記という記事に書いてあるとおりだが、上のようなエラー処理を行っている場合には例えば次のようなコードのクエリにx=<script>alert(1)</script>を指定した場合、XSSが発生する。
<?php $a = array(); $index = $_GET['x']; $b = $a[$index];
PHPで書いたコードを本番で運用する際には、display_errorsをoffにするのが定石なのにはこういう理由がある。しかし、冒頭で紹介したエラーハンドラを設定すると、display_errorsの値にかかわらず、必ずエラーが表示されてしまうため、セキュリティ上よろしくない。
まともなフレームワークを使っていれば、このへんは勝手にやってくれるはずだが、フレームワークなどを使わずにエラー処理する場合には次のようなコードを記述するのが望ましい。
<?php set_error_handler(function($errno, $errstr, $errfile, $errline) { // エラーを例外に変換する throw new ErrorException($errstr, 0, $errno, $errfile, $errline); }); set_exception_handler(function($e) { // display_errorsの値によって処理を変更する if (ini_get('display_errors')) { echo '<pre>' . $e . '</pre>'; } else { // エラーログに保存なりなんなりしてエラー画面表示 // ... readfile('path/to/error.html'); } });
set_error_handlerの中では、エラーをErrorExceptionに変換して投げている。これをやると、エラーメッセージが表示されるだけではなくスタックトレースも表示されるため開発時に役に立つ。set_exception_handlerでは、display_errorsディレクティブがonになっていれば普通に例外を表示するし、そうでない場合には固定のエラー画面を表示するようになっている。これであれば、display_errorsがoffになっている本番環境でも問題なく運用できる。
まとめ
- PHPのエラーをそのまま表示するのはセキュアではない