2 分間のツアーに参加する ×
スタック・オーバーフロー は、プログラマーとプログラミングに熱心の人 の質問と回答のサイトです。100% 無料で、登録不要です。

PHPでの

sprintf("SELECT * FROM table WHERE id = %d", $value);

sprintf("SELECT * FROM table WHERE id = %s", escape($value));

sprintf("SELECT * FROM table WHERE id = '%s'", escape($value));

の3つのコードについて、2行目のコードでSQLインジェクション脆弱性が生じ、1行目と3行目は問題がないのはどのような理由でしょうか

ここでescape()は値をSQLの文字列リテラル(のシングルクオーテーションの内側)としてエスケープするmysqli_real_escape_stringのような関数とします。

共有|この質問を改善する

1 件の回答 1

1番目

sprintf("SELECT * FROM table WHERE id = %d", $value);

$valueにどのようなデータが入っていようが、PHPの「親切な」型変換により数値に変換されます。escape()が無くても問題ありません。

"123"    => 123
"  123"  => 123
"123abc" => 123
" "      => 0
""       => 0
null     => 0

ただし、4つめ以降の例はアプリケーションとしては意図しない動作になるでしょうから、その観点では事前にバリデーションが必要です。

2番目

sprintf("SELECT * FROM table WHERE id = %s", escape($value));

1番目の%d%sに書き換えescape()を付加した例です。$valueはリテラルではなくSQL構文の一部として展開されます。例えば$value1 or 1 = 1であれば

SELECT * FROM table WHERE id = 1 or 1 = 1

となります。escape()は文字列リテラルのためのエスケープですから、この場合SQLインジェクション防止には役に立ちません。上記は適切にエスケープが行われた結果です。

3番目

sprintf("SELECT * FROM table WHERE id = '%s'", escape($value));

$valueは文字列リテラルとして展開されます。escape()で文字列リテラルとして正当な形式にエスケープされるので安全です。

以上はSELECTを例にしていますが、INSERTであってもサブクエリを用いたりRDBによっては複文が使えたりするので、同様に攻撃が可能です。

sprintfを使用したSQL組み立ての問題

このように、sprintfでのSQL組み立ては、

  • 数値型と文字型で書式指定子を確実に指定する。
  • %sで受ける場合は、'%s'とシングルクオーテーションを忘れてはいけない
  • エスケープ処理を確実に行わなければいけない(ただし%dで受けるときは必須ではない)

と言うことにプログラマが注意する必要があります。不注意によりこれらを間違えると脆弱性が生じます。

プレースホルダを使用する場合はSELECT * FROM table WHERE id = ?のように文字列であっても引用符無しで?を指定しますがこれに引っ張られて裸の%sを指定したり、'%d'を用いた際の型変換によるSQLインジェクション防止を拡大解釈し、sprintfを用いるときはエスケープ不要であると誤解している例もあります。

プレースホルダを用いたバインドメカニズムを使うべき、とされるのは、仕組み上安全なことが保証されており、プログラマの不注意により脆弱性が生じるようなことがないからです。そのあたりの詳細はIPAの安全なSQLの組み立て方を参照してください。

共有|この回答を改善する

回答

 
破棄

質問を投稿することで、プライバシー ポリシーおよびサービス利用規約に同意したものとみなされます。

求めていた回答ではありませんか? のタグが付いた他の質問を参照するか、自分で質問をする