- やさしいPHP やさしいシリーズ 単行本 – 2008/2/29
- やさしいPHP 第2版 (やさしいシリーズ) 単行本 – 2010/8/28
- やさしいPHP 第3版 (「やさしい」シリーズ) 大型本 – 2014/9/26
第2版ではクロスサイトスクリプティング(XSS)の説明が追加され、第3版ではXSSに加えSQLインジェクションの説明が追加されました。つまり、初版ではこれらの説明はなかったということです。
第3版におけるSQLインジェクションの対策方法はプレースホルダによるもので、結果として本書にSQLインジェクション脆弱性は見当たりません(パチパチパチ)。本書にはSQLの(文字列リテラルの)エスケープに関する説明はありませんが、これは、「PHPとセキュリティの解説書12種類を読んでSQLエスケープの解説状況を調べてみた」で報告したように最近のPHPの解説書のトレンドで、私の記事にも書いたようにPHP入門書レベルならそれで十分かと思います。最初のうちは、安全なプログラムを自然な形で書けることが重要だと思うからです。 ということで、以下を宣言させてもらいたいと考えます。
PHPの入門書ではSQLインジェクション脆弱性がないことが当たり前になった一方、XSSはどうでしょうか。本書第3版では、P362に以下の説明があります。
フォームからの入力情報に注意するこの後、XSSの攻撃例やHTMLエスケープの方法などが続きます…が、「フォームからの入力情報に注意」という表現にとても嫌な予感がしますね。「フォームからの入力」以外はどうなっているかと調べてみると、同書P400のSample8.phpにあるように、入力値以外にはHTMLエスケープ処理が入っていません。以下に引用しますが、入力した内容のデータベースへの登録と、データベースの内容の表示処理があります。
Webアプリケーションでは、セキュリティに注意しなければなりません。特にフォームから入力する場合には注意すべきことが多くあります。
フォームから入力された文字列がデータベースに登録され、それが表示される過程でHTMLエスケープは行われていません(上記赤字部分参照)。その結果、クロスサイトスクリプティング脆弱性が存在します。以下の文字列を製品名の欄に入力してやると…// 以下、データの追加 if(isset($_POST["insert"])){ $ipd = $_POST["insproduct"]; $ipc = $_POST["insprice"]; $qry ="INSERT INTO product(name, price) VALUES(:ipd, :ipc)"; $stmt = $db->prepare($qry); $stmt->bindParam(":ipd", $ipd); $stmt->bindParam(":ipc", $ipc); $stmt->execute(); } // 中略 $qry = "SELECT * FROM product"; $data = $db->query($qry); // 中略。以下、データの表示 while($value = $data->fetch()){ $id = $value["id"]; $name = $value["name"]; $price = $value["price"]; print "<tr><td><input type=\"radio\" name=\"delid\" value=\"{$id}\"/></td> <td>{$id}</td><td>{$name}</td><td>{$price}</td></tr>\n"; }
下記のようにJavaScriptが実行されます。<script>alert(1)</script>
ところで、この画面表示、よく見ると文字が化けています。調べてみると、本来UTF-8でレスポンスが送信されているのに、レスポンスに文字エンコーディングの指定がないので、ブラウザにShift_JISとみなされ、文字化けの原因になっています。これはXSSの原因になりえます。また、そもそも文字化けが起こるということは、アプリケーションとして不完全な状態です。
PHPの場合、HTTPレスポンスの文字エンコーディングを指定する方法には以下の3種類がありますが本書では特に説明されていません。
- header関数で指定する
- php.iniのdefault_charsetで指定する
- HTML内に指定する
header関数で指定する
header関数によりContent-Typeヘッダを出力してそこでcharsetを指定する方法がもっとも確実です。header('Content-Type: text/html; charset=UTF-8');
php.iniのdefault_charsetで指定する
php.iniにおいて以下の指定を有効にすることでも、同様の指定ができます。PHP-5.6.0以降ではこれがデフォルトになりました。default_charset = "UTF-8"
HTML内に指定する
HTMLのhead要素内で以下を指定することでもcharsetを指定できます。php.iniの変更ができない環境等では便利です。<meta charset="UTF-8">
結局XSS対策として最低限説明すべきことは何か?
今まで説明したように、「やさしいPHP」のXSS対策の解説は不十分です。それではどう書けばよいかですが、IPA「安全なウェブサイトの作り方 改訂 第7版」には、以下の説明があります(抜粋)。5-(i) ウェブページに出力する全ての要素に対して、エスケープ処理を施す。本書の場合、これらのいずれもが十分ではありません。
解説内:また、HTMLタグを出力する場合は、その属性値を必ず「"」(ダブルクォート)で括る
5-(viii) HTTPレスポンスヘッダのContent-Typeフィールドに文字コード(charset)を指定する。
まとめ
書籍「やさしいPHP 第3版」のSQLインジェクションとXSSの解説状況について報告しました。SQLインジェクションに関してはプレースホルダの使用により脆弱性や説明の誤りはありませんでした(パチパチパチ)。一方、XSSに関しては、htmlspecialcharsを用いてHTMLエスケープを行うという概要の説明は正しかったものの、その適用が十分ではありませんでした。実はこの問題は多くのPHP入門書に共通する問題です。対策自体は十分に行えている解説書でも、入力した文字列をまずHTMLエスケープしてから扱っている書籍も多く、「表示の際にエスケープする」という基本が実践できている書籍は、入門書にはあまりなく、中級者以上向けの書籍でないと解説されていない傾向があります。
しかし、せっかくPHPを勉強するのであれば、最初から正しい方法を学びたいものです。ということで、PHPの入門書を書く方には、「出力の際にHTMLエスケープ」を徹底していただきたいと希望します。
おまけ
今回紹介した以外に、書籍「やさしいPHP 第3版」には以下のセキュリティ上の問題があります。ファイルアップロードのサンプルにおいて、ファイル名の拡張子チェックなどがないので、そのまま使うと任意スクリプトのアップロードと実行ができる。すなわち、スクリプトインジェクション相当の脆弱性がある(10.6)
ファイルの読み書きのサンプルにディレクトリトラバーサル脆弱性がある。とくに、ファイルへの書き込みのサンプルが深刻で、任意のスクリプトをPHPファイルとして書き込めるのでスクリプトインジェクション脆弱性相当の危険性がある(12.2)。
外部コマンドの実行において、OSコマンドインジェクションの説明が一応あるが、エスケープ用関数としてescapeshellcmd()を紹介しているので、こちらで紹介しているような問題が残る(12.2)。
0 件のコメント:
コメントを投稿