Personal tools

PHPでのSQLインジェクション対策 - エスケープ・クォート編

第四企画 坂井 潔

ここではPHPでSQLインジェクション対策としてエスケープ・クォート処理を行うケースを説明します。なおSQLインジェクションの簡単な説明や、プレースホルダを用いてより効果的な対策を行うケースに関してはプレースホルダ編を参照してください。


エスケープとは?

それではエスケープ処理とはなんでしょうか? 分かりやすいケースとして、文字列を扱う場合を説明します。

例えば「名前がO'Reillyの行をテーブルusersから取得する」あるいは「テーブルusersに、名前はO'Reilly、メールアドレスはo'reilly@example.como'reilly_mobile@example.netの2つを改行で繋げたデータを、行として挿入する」と言ったSQLをつくると、この命令文全体が1つ文字列になります。その命令文としての文字列の中に、値としての文字列を埋め込む場合には、それが値であるということを明示しなければなりません。

そのためにSQLでは、値としての文字列(文字列リテラル)をシングルクォートで囲む(クォートする)ことで表現します。ただしこれだけでは十分ではありません。先程の命令文の内の「値としての文字列」を単にシングルクォートで囲んだ場合、SQLは

SELECT * FROM users WHERE username = 'O'Reilly';
INSERT INTO users (username, email) VALUES ('O'Reilly', 
'o'reilly@example.com
o'reilly_mobile@example.net');

となってしまいます。SQLパーサは、'O'までを文字列の値として認識できても、その後のReillyで始まる部分をどのように解釈して良いのかが分からなくなってしまいます。プレースホルダ編でも説明しましたが、だからこそ「単にシングルクォートで囲む」という対処はSQLインジェクションに対して一番危険で間違った方法なのです。

SQLでは、シングルクォートは文字列の開始・終了の合図、すなわち特殊文字であるため、これを文字列リテラルとして認識させるためには、このシングルクォートをなにかで置き換え、開始・終了の合図であるという特殊な意味を奪うことが必要になってきます。この置き換えの操作を「エスケープする」と言います。

SQL標準で定義されているエスケープ方法は、単純に1つのシングルクォートを2つに重ねることです。上記のSELECT文の例で言えば、

SELECT * FROM users WHERE username = 'O''Reilly';

となります。つまり文字列「O'Reilly」を文字列リテラルとしてエスケープすると「O''Reilly」に、そしてそれをクォートすると「'O''Reilly'」になるわけです。

SQL非標準のエスケープとstandard_conforming_strings

ただしPostgreSQLでは、このSQL標準のエスケープ方法に加え、\n(改行)、\t(タブ)などのバックスラッシュ+1バイトの一部を特殊文字として扱うようになっています。これはpostgresql.confでのstandard_conforming_stringsの設定がoffの時に機能し、PostgreSQLはバージョン9.0以前ではデフォルト設定がoffなのです。

このような設定の時には、これらの特殊文字から特殊な意味を奪い、通常の文字列としても表現できるようにするため、バックスラッシュもエスケープ文字として認識するようになっています。なお、バージョン9.1以降ではstandard_conforming_stringsがonがデフォルトになり、この場合はSQL標準の通り、バックスラッシュは通常の文字と扱われ、エスケープ文字としては機能しません。standard_conforming_stringsに関してはPostgreSQL 9.1 の新機能 「互換性に関する注意」も参照してください。

エスケープ対象の文字
standard_conforming_strings'...' でクォートE'...' でクォート
off (~9.0のデフォルト)' と \ ※1' と \
on (9.1~のデフォルト)' のみ' と \
※1 バックスラッシュ(\)をエスケープした場合にはWARNINGが発生します。

standard_conforming_stringsがoffの場合には、以下の2つの文

SELECT 'o\'reilly@example.com\no''reilly_mobile@example.net';
SELECT 'o''reilly@example.com
o''reilly_mobile@example.net';

は同じ意味になります。standard_conforming_stringsがonであれば、これの2つの文は別の意味になるどころか、1つめの文はバックスラッシュは通常の文字列にすぎないので、'o\'で文字列が終了してしまい、SQL構文としての整合性すらも無くなってしまいます。

ブログラムの書き方によっては、standard_conforming_stringsがonの時でも、どうしてもバックスラッシュ+1バイトを特殊文字として扱いたい、そしてバックスラッシュをエスケープ文字として扱いたいという場合もあるかもしれません。そのような時には、'...'とではなくE'...'とクォートすることで、これが(SQL標準のエスケープ・クォートではなく)バックスラッシュもエスケープ文字として扱い、\nあるいは\tといった文字列を特殊文字として扱うクォートだということを明示することになります。

SELECT E'o\'reilly@example.com\no''reilly_mobile@example.net';
ただしこれはSQL標準のエスケープ方法ではありません。バックスラッシュ+1バイトの特殊文字は通常のバイナリで書き直し、自作のエスケープ・クォートではなく次ページ以降で説明するスクリプトにエスケープ・クォートさせることをおすすめします。

それでは実際にエスケープ・クォートの例を見ていくことにしましょう。

 

You are here: Home 読み物 入門者向けの記事 PHPでPostgreSQLを使ってみよう PHPでのSQLインジェクション対策 PHPでのSQLインジェクション対策 - エスケープ・クォート編