(Last Updated On: 2018年8月13日)

SQLのリテラルはエスケープが必要であることは広く認知されていると思います。しかし、識別子のエスケープはあまり広く認知されていないように思います。

PostgreSQLの場合、識別子のエスケープAPI(libpqのPQescapeIdentifier)が提供されておりPHPでもpg_escape_identifier()として利用できます。PostgreSQLの場合は”(ダブルクオート)で識別子を囲むことにより、ダブルクォート無しでは利用できない文字(例えば日本語)も識別子に利用できるようになります。

http://www.postgresql.org/docs/9.3/static/sql-syntax-lexical.html

SQLリテラルのシングルクォートをシングルクォートでエスケープするように、SQL識別子ではダブルクォートをダブルクォートでエスケープします。

エスケープ前の文字列

Example " Identifier

pg_escape_literal()でエスケープ後

"Example "" Identifier"

pg_escape_literal()でエスケープ処理後の文字列はそのまま識別子として利用できるよう、自動的に”(ダブルクォート)で囲まれます。この他にも文字エンコーディングがUTF−8の場合はUnicodeで指定する

U&"d\0061t\+000061" - "data"と同じ

も利用できます。エスケープ文字の指定や$を使ったエスケープも可能ですがここでは省略します。

MySQLもPostgreSQLと同様に識別子のクオートをサポートしています。

http://dev.mysql.com/doc/refman/5.7/en/identifiers.html

MySQLの場合、は ` (バッククォート)で識別子を囲みます。

エスケープ前の文字列

Example ` Identifier

クエリにそのまま利用可能にエスケープ後

`Example `` Identifier`

MySQLでもANSI QUOTES SQLモードに設定されている場合、PostgreSQLと同様に”(ダブルクォート)で囲んだ文字列が識別子として認識されます。デフォルトのMySQLではダブルクォートで囲んだ文字列もリテラルとして認識されますが、ANSI QUOTES SQLモードの場合は必ず ’ (シングルクォート)で囲まなければなりません。

The ANSI_QUOTES mode causes the server to interpret double-quoted strings as identifiers. Consequently, when this mode is enabled, string literals must be enclosed within single quotation marks. They cannot be enclosed within double quotation marks. The server SQL mode is controlled as described in Section 5.1.7, “Server SQL Modes”.

Webアプリケーションに限らず、出力先の出力仕様を正しく理解していないと正しい出力とならず、インジェクション脆弱性を生む原因になります。出力先には幾つものエスケープパターンがある場合も多いです。全てのエスケープ方法を理解する必要はありませんが、エスケープ方法が利用できる条件は正確に理解しておく必要があります。

PostgreSQLもMySQLもコンソールからの特殊文字が入力できるよう、\ によるエスケープをサポートしています。テキスト化した初期データを準備する場合には、これらを知っていると役立つ場合もあると思います。

PHPのセキュリティ入門書に記載するコンテンツのレビューも兼ねてブログを書いています。コメント、感想は大歓迎です。

投稿者: yohgaki