教えて!gooに「PHPのSQLインジェクションはsprintf?」という質問が投稿されました。
以下のようなコードがあり、nameは画面入力なのでSQLインジェクションが起こるのでは?
と作成者に確認したところ、"%s"してあるから大丈夫との返事をもらいました。
ネット調べるとmysql_real_escape_stringでエスケープしてから"%s"で変換すれば大丈夫といった内容は見つけたのですが、mysql_real_escape_stringなど不要との返事をもらいました。
なぜ?と聞くとそういうものだとしか回答がありません。
ひどいですね。これは質問者が正しく、sprintfの%sで受けただけでは、SQLインジェクション脆弱性となります。
しかし、どうしてこのような間違った知識が出てきたのかと考えるに、数値を%dで受ける場合と混乱したのではないかと憶測しました。数値の場合、書式%dで受けていれば、仮に攻撃コードが入力されたとしても、%dで整数に強制変換されるので、SQLインジェクション攻撃はできなくなります。
…とこれで終わりにしても良いのですが、sprintfと言えば、「書式文字列の問題(CWE-134)」を連想します。書式文字列の問題とは、sprintfなどに与える書式文字列を外部から操作できることによって脆弱性が生じるものです。Webアプリケーションの場合、書式文字列の問題を見かけることはあまりありませんが、この機会に考えてみました。以下のSQL組み立てを考えてみます。$kindは文字列、$ageは整数です。
$kind = ... $age = ... $e_kind = mysql_real_escape_string($kind); $sql = sprintf("SELECT * FROM employee WHERE kind = '$e_kind' AND age >= %d", $age);
本来であれば、$e_kind($kindをエスケープしたもの)は%sで受けるべきところですが、書式文字列に埋め込んでいます。これは良くないのですが、エスケープ済みなので攻撃はできないように見えますね。
でも、駄目です。$kindとして %s を指定したらどうでしょうか。この場合、sprintfは以下が実行されます。
$sql = sprintf("SELECT * FROM employee WHERE kind = '%s' AND age >= %d", $age);
$ageを%sで受けることになるので、$ageに攻撃文字列が指定された場合、そのままの形でSQL文の一部になります…しかし、実はこれだとPHPのエラーになります。
PHP Warning: sprintf(): Too few arguments in xxxx.php on line xx
書式が%sと%dの2つあるのに、パラメータが$age 1つなので、警告が表示され、sprintfの戻り値としてfalseが返ります。
では、大丈夫なのでしょうか? 実はこのエラーを回避する方法があります。%sの代わりに、%1$sを指定するのです。これは、書式文字列上の位置に関わらず1番目のパラメータを受けるという意味です。この場合、以下のsprintfが実行されます。
$sql = sprintf("SELECT * FROM employee WHERE kind = '%1$s' AND age >= %d", $age);
%1$sと%dは、いずれも$ageを受けます。これで攻撃が可能になりましたので、SQLインジェクション攻撃を完成させましょう。
$kind = '%1$s'; $age = "' UNION SELECT email, password, 0, 0 FROM members #"; $e_kind = mysql_real_escape_string($kind); $sql = sprintf("SELECT * FROM employee WHERE kind = '$e_kind' AND age >= %d", $age);
$kindの中にはエスケープ対象となる文字列は含まれないので、そのまま書式に指定されます。$ageの方は、整数を想定している変数ですが、パリデーションがない場合そのままSQL文に組み込まれます。実行されるSQL文は下記となります。
SELECT * FROM employee WHERE kind = '' UNION SELECT email, password, 0, 0 FROM members #' AND age >= 0
MySQLの場合、# 以降はコメントとして無視されますので、上記はUNION SELECTにより別のテーブルの内容が表示されてしまう、という攻撃例です。
ではどうすればよいかというと、下記となります。
- sprintfなどの書式文字列は固定として外部から指定できないようにする
- SQL文は文字列連結やsprintfなどで組み立てずにプレースホルダを使う
PS.
回答者の方がいずれも「今時のSQLインジェクション対策はプレースホルダが常識」のように回答されていて力強く感じました。本当にそうだとよいのですが
[PR]SQLインジェクション対策に有効なWAF SiteGuard。導入のご相談はHASHコンサルティングまで
yushi reblogged this from ockeghem
stainless-note reblogged this from ockeghem
wenoog liked this
tommby reblogged this from ockeghem
send-blog liked this
kamito reblogged this from ockeghem
lingua-latina reblogged this from ockeghem
cuon reblogged this from ockeghem
atakigu reblogged this from ockeghem
gearmann liked this
lollapal00za reblogged this from ockeghem
littlepresident liked this
act2012bl reblogged this from ockeghem
atm09td reblogged this from ockeghem
dotthx reblogged this from ockeghem
dotthx liked this
sarabandejp liked this
grimrose7 liked this
inchiki reblogged this from ockeghem
yasu-sue liked this
ockeghem posted this