2011年9月26日月曜日

PostgreSQLは標準でバックスラッシュをエスケープしない仕様になった

PostgreSQL9.1の仕様変更にて、デフォルト時の設定として、standard_conforming_stringsがonとみなされるようになりました。この仕様変更により、デフォルト設定でのPostgreSQLは、バックスラッシュをエスケープする必要がなくなり、ISO規格のSQLと同様のエスケープルール(シングルクォートを重ねるのみ)となります。

PostgreSQLの文字列リテラルは、元々MySQL同様に、バックスラッシュをエスケープする仕様でした。その後、リリース8.1にて、設定パラメータ standard_conforming_strings が追加され、この値が on の場合、バックスラッシュをエスケープしない(ISO規格と同様の)仕様となりました。従来のリリースでは、standard_conforming_stringsを指定しない場合offとみなされていました。これは、後方互換性維持のためでしょう。

リリース8.1のドキュメントには以下のように記述されています。
standard_conforming_stringsの値は読み取りのみです。アプリケーションでは、この値を読み取ることで、どのようにバックスラッシュが解釈されるかが分かります。(また、このパラメータが存在するかどうかで、E''文字列構文がサポートされているかどうかが分かります。)今後のリリースでは、standard_conforming_stringsは真になる予定です。
ここで、E''文字列構文という用語が出てきました。これは、PostgreSQL固有の機能で、standard_conforming_stringsの設定に関わらずバックスラッシュによるC言語風のエスケープを行う文字列形式です。

ここで、standard_conforming_strings設定と、バックスラッシュのエスケープ要・不要の関係を下表にまとめました。

standard_conforming_strings設定とバックスラッシュのエスケープの関係
standard_conforming_stringsリリース9.0.4以前リリース9.1以降
offエスケープ要エスケープ要
指定無しエスケープ要エスケープ不要
onエスケープ不要エスケープ不要

表からも分かるように、PostgreSQLリリース9.1にバージョンアップして挙動が変わるのは、standard_conforming_stringsの設定をしていない場合です。この場合は、standard_conforming_stringsをoffにすることで従来通りの挙動となります。

しかし、standard_conforming_stringsの設定を変更しない場合は、既存のアプリケーションはどのような影響を受けるでしょうか。以下、PHPとPerlで記述されたアプリケーションについて、この変更の影響を検討してみます。

PHPのPostgreSQL関数(pg_xxxx)やPDOなどには、SQL文字列のエスケープ用の関数が用意されています。PostgreSQL関数ではpg_escape_string関数、PDOではquoteメソッドが該当します。これらを使用している場合、standard_conforming_stringsのオプションを自動的に反映して、適切にエスケープしてくれます。
DBI/DBDのPgやPgPPを使っている場合、quoteメソッドで文字列をエスケープしている場合も同様です。
これらの挙動を「a\'」という文字列がどうエスケープされるかによって調査しました。

standard_conforming_stringspg_escape_stringquote(PDO)  quote(Pg)quote(PgPP)
offa\\'''a\\'''E'a\\'''E'a\\\''
指定無しa\'''a\'''E'a\\'''E'a\\\''
ona\'''a\'''E'a\\'''E'a\\\''
※各モジュールのバージョン
PHP: 5.3.8
Pg: 2.18.1
PgPP: 0.08

quoteという名前のメソッド(PDO, PG, PgPP)は、いずれもエスケープするだけでなく、シングルクォートで文字列を囲むことにご注意下さい。
上表から、これらの関数・メソッドを使っていれば、文字列は正しくエスケープされることが分かります。pg_escape_stringとPDOのquoteメソッドは、通常の文字列リテラル形式を用い、バックスラッシュのエスケープをstandard_conforming_stringsに応じて切り替えています。
これに対して、PerlのPgとPgPPは、E''文字列形式を用いることによって、standard_conforming_stringsの影響を回避しています。また、プレースホルダを使ってSQLを呼び出している場合も、standard_conforming_stringsの影響は受けません。

一方、エスケープにaddslashesや自作のエスケープ関数を用いている場合は、対応が必要となります。また、PgPPの古いバージョンを使っている場合もstandard_conforming_stringsの設定を考慮しないので注意が必要です。

既存アプリケーションのリリース9.1対応では、standard_conforming_stringsの意味を変えない方が無難だと思います。このため、既存アプリケーションでPostgreSQLのバージョンを9.1以降に変更する場合は以下のようにすればよいでしょう。
  • standard_conforming_stringsを明示している場合はそのままでよい
  • standard_conforming_stringsを明示していない場合はoffを指定する
これにより、standard_conforming_stringsのデフォルト値の変更を吸収できます。

一方、新規アプリケーション場合は、standard_conforming_stringsを指定しない(あるいはonにする)と良いでしょう。その理由は、こちらの方がISOのSQL標準であり、またバックスラッシュをエスケープ対象にすると、セキュリティ上の問題になりやすいからです。MySQLとの互換性を気にする人もいるでしょうが、PDOやDBIなどの抽象度の高いライブラリを使って記述することにより、エスケープ対象の文字をアプリケーション側で意識する必要はなくなり、互換性も向上します。
さらに言えば、プレースホルダを用いてSQL呼び出しすることを強く推奨します。これにより、そもそも文字列リテラルのエスケープが必要なくなり、性能・互換性・セキュリティともに改善されます。

[PR]
安全なWebアプリケーションの作り方」電子書籍版9月28日(水)販売開始します。くわしくはこちら

0 件のコメント:

コメントを投稿

フォロワー