TOP書籍連動> プリペアードクエリによるリスクの回避
SQLインジェクション
SQLインジェクション

第1回:SQLインジェクションによるデータベース操作

著者:Ilia Alshanetsky   2006/1/18
前のページ  1  2  3
プリペアードクエリによるリスクの回避

   プリペアードクエリ(プリペアードステートメントともいいます)によって、前述のリスクのほとんどを回避することができます。プリペアードクエリは、クエリのいわば'テンプレート'です。クエリの構造をあらかじめ定義して変更できないようにしておくのですが、その中には実際のデータを配置するプレースホルダが含まれています。

   プレースホルダでは、例えばintは整数データ、textは文字列というように、データベースがそのデータを厳密に解釈するように型が特定されています。つまり、text型のプレースホルダならいつでも文字列として解釈することで、クエリのスタックを使ったSQLインジェクションのような攻撃を防ぐのです。

   プレースホルダの型と中のデータが一致しなければ実行エラーとなり、クエリをより厳密にチェックすることができます。クエリの安全性確保に加えて、プリペアードクエリにはパフォーマンスを向上させる効果もあります。

   プリペアードクエリはそれぞれ1度解析し、コンパイルされますが、何度でも再利用することができます。大量のINSERTが必要なら、あらかじめコンパイルされたクエリを使うことで時間を節約できるのです。

   プリペアードクエリは非常に簡単です。以下の例を見てみましょう。

pg_query($conn, "PREPARE stmt_name (text) AS "." SELECT * FROM users WHERE name=$1");
pg_query($conn, "EXECUTE stmt_name ({$name})");
pg_query($conn, "DEALLOCATE stmt_name");

   PREPARE stmt_name(text)AS…の部分でtext型の値を1つ持つstmt_nameという名前のプリペアードクエリを作ります。ASに続くキーワード全体が実際のクエリで、$1がtext型のプレースホルダです。

   クエリに2つ以上の値が含まれる場合は、それぞれの型を順にコンマで区切って列挙し、各プレースホルダは$1、$2、…というようにして、PREPARE stmt_example(text,int)AS SELECT * FROM users WHERE name=$1 AND id=$2 といったように使います。

   1度PREPAREでコンパイルすれば、EXECUTEでプリペアードクエリを実行することができます。続きの2つの文を見てみましょう。指定した名前のプリペアードクエリ(ここではstmt_name)が実行されます。実際の値のリストが括弧で囲われています。

   プリペアド文を使い終わったら、DEALLOCATEで廃棄します。プリペアードクエリを廃棄し忘れると、次のPREPARE 文の実行で失敗する可能性があります。これは永続的接続が使われていて、リクエストをまたがってプリペアードクエリが存在した場合によく発生するエラーです。

   例えば、あるプリペアードクエリが存在するかどうかチェックする仕組みがないのにやみくもにクエリを作っている場合、同名のクエリが存在すればエラーになります。

   プリペアードクエリはすばらしいものですが、すべてのデータベースでサポートされているわけではありません。そうはいっても、このエスケープ機能は使っていくのがよいでしょう。

前のページ  1  2  3


Ilia Alshanetsky
著者プロフィール
Ilia Alshanetsky
PHP開発チームの活動メンバーの1人であり、現在のPHP 4.3.X.のリリースマネージャー。また、オープンソース掲示板FUDフォーラム(http://fud.prohost.org/forum/)をはじめとする数多くのプロジェクトにも貢献している。


INDEX
第1回:SQLインジェクションによるデータベース操作要
  SQLインジェクションとは
  独自のエスケープ機能を呼び出す際の注意点
プリペアードクエリによるリスクの回避