私のプレゼンテーションSQL Injection Myths and Fallacies をご覧ください。
または、私がプレゼンテーションを行っているウェビナーの録画をご覧ください: http://www.percona.com/webinars/2012-07-25-sql-injection-myths-and-fallacies (無料ですが、登録が必要です)。
次のようないくつかの例を示します。
ユーザーがパスワードを変更できるページがあり、ユーザーが次のような URL を使用してそのページにアクセスするとします。
http://example.com/changepass.php?acctid=1234 &pass=xyzzy
(もちろん、通常は POST リクエストを使用しますが、簡単に説明するために、この GET 形式を使用します。)
そのページのスクリプトには、次のような SQL 更新ステートメントがあります。
UPDATE Accounts
SET password = SHA2('$password')
WHERE account_id = $account_id
悪意のあるユーザーが次のようにパラメーターを巧妙に設定した場合はどうなるでしょうか。
http: //example.com/changepass.php?acctid= 1234 または TRUE
&pass= xyzzy'), admin=('1
次に、SQL は次のステートメントを実行することになります。
UPDATE アカウント SET password = SHA2(' xyzzy'), admin=('1 ') <br> WHERE account_id = 1234 OR TRUE
パスワードを設定するだけでなく、テーブル内の別の列を設定して、ユーザー管理者権限を付与します。また、WHERE 句への挿入により、ログインしているユーザーだけでなく、サイト内のすべてのユーザーに変更が適用されます。
パラメータの値は次のとおりです。SQL 式では、1つのパラメータが常に1 つの値として扱われます。
つまり、そのパラメーターに渡す文字列にインジェクションを悪用する SQL 構文が含まれていたとしても、パラメーターを使用すると、単一の文字列または数値以外のものとして解釈できないことを意味します。OR
そのため、文字列を終了したり、SQL ステートメントの構文やセマンティクスを変更するような括弧やその他のものを含めたりすることはできません。
したがって、バインドされたパラメーターを使用すると、次のようになります。
UPDATE アカウント SET password = SHA2(' xyzzy\'), admin=(\'1 ') <br> WHERE account_id = ' 1234 OR TRUE '
スペースや引用符が含まれているパスワードは変に見えるかもしれませんが、SQL の意味は変わりません。そして、WHERE 句はその文字列の整数値を使用します。これはちょうど 1234 になります。
あなたのコメントについて:
コードのすべての行をトレースしたわけではありませんが、実行のすべての段階で SQL ステートメントの文字列を値から分離したままにしていると思います。ある時点で、SQL が解析され、何らかの内部形式 (人間が判読できるテキストではない) で表されます。次に、パラメーター値がこれに結合されますが、それらは強制的に単一の構文要素になります。ポイントは、クエリが解析された後に結合されるため、パラメーター値が構文を変更する方法がないということです。
1 つの例外: 一般的なクエリ ログは、指定された実行のパラメーターと組み合わせてクエリをログに記録する目的で、値を元の SQL 文字列に補間します。ただし、この結合されたクエリ文字列は実行されません。