SQLインジェクション — 入力欄に呪文を混ぜて扉を破る
ログイン画面の入力欄は、ただの「文字を打つ箱」に見えます。でもその文字が、裏側でデータベースへの命令文(SQL)の一部として組み立てられているとしたら? 攻撃者は入力欄に呪文を混ぜて、命令文そのものを書き換えてしまえます。これがSQLインジェクションです。
下の図1は、わざと脆弱に作ったログインフォームです。ユーザー名の欄にいろいろ入れて「ログイン」を押し、組み立てられるSQLがどう変わるかを見てください。最後に、正しい防ぎ方も同じ画面で試せます。
「データ」のはずが「命令」になってしまう
脆弱版(①)の正体は、こういうコードです — "SELECT * FROM users WHERE name='" + 入力 + "'"。入力をそのまま文字列として連結しているので、' OR '1'='1 と打つと、クロージングの'でユーザー名の囲みを勝手に閉じて、その後ろのOR '1'='1'を命令文の一部として注入できてしまいます。「1=1」は常に真なので、パスワードを知らなくても全行が条件に一致し、ログインが突破されます。
もっと危ないのが;(命令の区切り)を使う攻撃です。'; DROP TABLE users;-- を注入されると、ログインのついでにテーブルごと削除される命令が実行されかねません(末尾の--は以降をコメント化して、残りのSQLを無効にする呪文です)。
根っこの原因は1つ。「ユーザーが入れたデータ」と「プログラムが書いた命令」が、同じ1本の文字列に混ぜられていること。境界が文字の見た目(クォート)だけで決まるので、攻撃者にそのクォートを乗っ取られると、データのつもりが命令に化けるのです。
防ぎ方:データと命令を最初から分けて渡す
安全版(②)で使っているのがプレースホルダ(プリペアドステートメント)です。命令文には穴(?)だけを開けておき、ユーザーの入力は後から「これはデータです」と明示して別ルートで渡します。こうするとデータベースは、入力に'や;が含まれていても、それを命令とは絶対に解釈せずただの文字として扱うので、いくら呪文を混ぜても命令文の構造は変わりません。図1の②で同じ攻撃を試すと、はじかれるのが確かめられます。
- SQLインジェクション
- 入力欄からSQL命令を注入し、認証突破やデータ破壊・窃取を行う攻撃。
- プレースホルダ
- 命令文に穴(?)を開け、値を後から「データ」として渡す書き方。SQLi対策の本命。
- エスケープ
- 特殊文字を無害化する処理。補助的だが、プレースホルダの徹底が第一。
まとめ
SQLインジェクションは「データのつもりの入力が、命令文に化ける」攻撃です。原因はデータと命令の混在、対策はプレースホルダで両者を最初から分けて渡すこと。入力値のチェックやエスケープも役立ちますが、本命は常にプレースホルダです。「ユーザー入力を文字列連結でSQLに入れない」— これだけ守れば、大半の攻撃は成立しません。