blogこの広告は、90日以上更新していないブログに表示しています。2009-07-11PHP逆引きレシピは概ね良いが、SQLインジェクションに関しては残念なことに404方面でも絶賛されていたPHP逆引きレシピを購入した。本書はとても丁寧な仕事で素晴らしいと思ったが、セキュリティに関しては若干残念な思いをしたので、それを書こうと思う。目次は以下のようになっている。オフィシャルサポート本書は、タイトルの示すように、コレコレしたいという目的ごとにPHPでの書き方が書かれている。ある逆引き辞典タイプの本だが、類書に比べて丁寧に書かれている印象を受けた。私が感心したのは、PHP4.4.9、PHP5.2.9、PHP5.3(RC2-dev)での対応を書いてあるだけでなく、『本書では、レンタルサーバー「さくらインターネット」「XREA」「ロリポップ」でサンプルスクリプトの動作確認を行い、その結果を以下のマークで表しています』というところで、丁寧な仕事ぶりだと思った。第8章の「セキュリテイ」についても概ねよく書けている。先行する仕事をよく調べつつ、その内容をしっかり咀嚼した上で書いてあるという印象だ。書かれている内容すべてに同意するわけではないが、少なくとも、本書で書かれている方法で書いてセキュリティホールが混入するということは、ほとんどないのではないか。本書で省略された内容についても、HTTPヘッダインジェクションくらいで*1、PHPで問題になりやすいヌルバイト攻撃や、文字エンコーディングの問題、インクルード攻撃などもしっかり記述してある。筆者たちの努力に、まずは敬意を払いたい。SQLアクセスに関しては、残念なところがある。本書のP614には以下のように正しい指摘がある。SQLインジェクションへの対策は、プリペアドステートメントやプレースホルダの仕組みを持つ関数を使うことです。その後のサンプル06-2.phpも、以下のようにバインド機構を用いたスクリプトが示されている。準備したクエリに変数をバインドします。文字列(s)であることを指定しています。mysqli_stmt_execute($stmt);しかし、その前の第6章のP524に書かれているサンプルスクリプトは以下のようにエスケープが使われている。SQL文を作成します。);エスケープを使うこと自体は間違いではないが、第8章のセキュリティの説明では第一にプリペアドステートメントを推奨しているのであれば、サンプルもそちらで統一できるとよかった。上記のサンプルには気になるところがある。リテラルをわざわざエスケープしていること自体はよいことだろう。ユーザ入力であろうとなかろうと、常にエスケープするという正しい習慣を身につけることに役立つからだ。問題は、列idに関してだ。この列は整数型である。テーブルexampleの定義は以下のようになっている(本書P520)。CHARSET=utf8;そして、エスケープ後のSQLは以下のようになる。'Ruby'このように、数値をクォートしてエスケープする方法は、従来から大垣靖男氏が主張している方法だ。文字列,整数などデータ型に関わらず変数すべてを文字列としてエスケープすることにより,SQLインジェクションを100%防ぐことが可能となります。http://gihyo.jp/dev/serial/01/php-security/0005?page=2今まで追随者を見かけなかったが、最近になって大垣氏以外の方がこの方法を紹介する例が増えてきているように思う。このパターンを以下のように一般化すると、どうだろうか。SQL文を作成します。);$idに元々整数型の値が入っていた場合に、このスクリプトは整数と文字列の間で、何度も型変換が行われる。右向き矢印「→」の数だけ型変換が行われるので、上記の場合は4回型変換が行われ、整数と文字列の間を行ったり来たりすることになるのだ。これはいささか筋の悪いプログラムと言えるだろう。SQLの文字列→数値の暗黙の型変換は色々問題がある。ockeghem(徳丸浩)の日記で紹介した。T.Teradaの日記にて補足いただいた内容によると、DB2ではそもそも文字列→数値の「暗黙の型変換」はエラーになるとのことである。高速化プチBKで紹介されている。私もあらためてMySQL5.1にて追試したところ、数値をクォートしない場合が0.23秒、クォートする場合が0.41秒と、倍近い差となっている。徳丸浩の日記(2007-09-24)にてまとめている。思うに、上記は以下のように書けばよいのではないか。);このようにすれば、何度も型変換することはないし、非常に読みやすい。効率も良い(悪くならない)。この書き方の良いところとして、ソースコードのチェックがやりやすいこともある。sprintfの書式文字列は、%dはクォートしない、%sはクォートする、この二点を確認する。パラメータのところでは、(int)のキャストか、mysql_real_escape_stringによるエスケープのどちらかを実行していることを確認する。これで、SQLインジェクション対策については問題ないのだ。心配なのは、数値を'%s'で受けたり、文字列を%dで受けることだが、下表のようにSQLインジェクションにはならない。文字列を%dを受けると当然ながら処理はおかしくなるが、これはこの方法固有の問題ではなくて単なるバグだ。本書では、これ以降のところでも、概ね似たようなサンプルが並ぶのだが、とくにおかしいのがP548の「テーブルを作成したい」に出てくるサンプルだ。これは以下のようになっている。作成するテーブルの中身を変数に代入します。テーブルを追加するためのSQL文を作成します。TABLE文について、列定義の部分のみ$newTableとして切り出しているが、それをSQLとして組み立てる際に、mysql_real_escape_string関数でエスケープしている。SQLのエスケープは、SQLの文字列リテラル内をエスケープすべきなのであって、このようなSQL断片に対してmysql_real_escape_stringを通してしまうと、処理がおかしくなる。このサンプルの場合は正常に動作するが、それは偶然に過ぎないと考えた方がよい。うまく行かない例として、MySQLの場合、列定義にコメントがつけられる。comment');このような場合に括弧の中身を無理にエスケープすると、次のように文法違反となる。comment'');COMMENTなんて使わないかもしれないが、そもそも列定義の部分をエスケープをする必要などまったくないのだ。本書は750ページ近いボリュームで、\2,600というお値打ち価格であるにも関わらずとても手の掛かった丁寧な仕事でお買い得なのだが、それだけに、上記はとても残念な気がした。本書の読者の参考になれば幸いである。対するSQLインジェクション対策のまとめSQLの暗黙の型変換はワナがいっぱいquoteメソッドの数値データ対応を検証する追記(2010/3/19)IPA非常勤研究員として「安全なSQLの呼び出し方」執筆に参画いたしました。数値に対するSQLインジェクション対策も説明していますので、あわせて参考にいただければと思います。セキュリティ対策が確実に実施され...2011-09-26『よくわかるPHPの教科書』のSQLインジェクション脆弱性このエントリでは、数値型の列に対するSQLインジェクションにつ...2011-08-23もし『よくわかるPHPの教科書』の著者が徳丸浩の『安全なWebアプリケーションの作り方』を読んだらたにぐちまことさんの書かれた『よくわかるPHPの教科書(以下、...2011-04-07ライザムーン(LizaMoon)攻撃に対してもWAFは有効ライザムーン攻撃に対する行き届いた解説を読みました。