SQLインジェクション(SQLi)
SQL Injection (SQLi)
ユーザー入力をSQL文に文字列連結することで、攻撃者にSQLの構造を改変されてしまう攻撃。認証回避・情報窃取・データ破壊につながる。根本対策はパラメータ化クエリ(プリペアドステートメント)。OWASPでは A03 Injection。
SQLインジェクションとは(具体例で理解)
**SQLインジェクション(SQLi)**は、ユーザー入力をSQL文にそのまま混ぜ込むことで、攻撃者にSQLの意味を書き換えられてしまう攻撃です。
危険なコードの例(やってはいけない書き方):
query = "SELECT * FROM users WHERE name = '" + 入力 + "'"
ここで入力欄に普通に tanaka と入れれば問題ありません。でも攻撃者が次のように入力したらどうなるでしょう:
' OR '1'='1
すると組み立てられるSQLは:
SELECT * FROM users WHERE name = '' OR '1'='1'
'1'='1' は常に真なので、全ユーザーが該当してしまいます。ログイン処理ならこれで認証突破。さらに巧妙な入力で、全データの抜き取りやテーブル削除(DROP TABLE)まで可能になります。
問題の本質はXSSと同じ構造です: 『データ』であるべき入力が、『命令(SQL)』として解釈されてしまっている。
根本対策: パラメータ化クエリ
唯一にして最強の対策が**パラメータ化クエリ(プリペアドステートメント)**です。考え方を絵にすると:
図を描画中...
ポイントは、SQLの『骨組み(構造)』を先に確定させてから、ユーザー入力を『値』としてはめ込むこと。WHERE name = ? の ? の部分に入力を渡すと、DBドライバが入力を必ず『ただの値』として扱い、絶対に命令として解釈しません。だから ' OR '1'='1 を入れても、それは『そういう名前の文字列を探す』だけになり、構文は壊れません。
たとえ話: 文字列連結は『書きかけの契約書に相手が好きな条文を書き足せる』状態。パラメータ化は『契約書の文面は確定済みで、空欄に相手の名前を記入してもらうだけ』。空欄に何を書かれても契約の本文は変わりません。
補助的な対策(多層防御)
- ORM: オブジェクトとSQLを橋渡しするライブラリ。多くは内部でパラメータ化クエリを使うので安全側に倒れます。ただし生SQLを書ける抜け道があり、そこで文字列連結すれば台無し。『ORMだから安全』と過信しない。
- 入力検証: 型・長さ・形式をチェック。補助としては有効ですが、これだけでは根本対策になりません(検証をすり抜ける入力は常にありうる)。
- DBアカウントの最小権限: アプリ用DBユーザーに
DROP権限を与えなければ、万一SQLiされてもテーブル削除は防げる。被害を限定する『最後の砦』。
つまずきポイント
- 『根本対策は?』と聞かれたら即答で『パラメータ化クエリ』。『入力をサニタイズする』は補助であって根本ではありません(面接で差がつくポイント)。
- テーブル名や列名はパラメータ化できない。
?で渡せるのは値だけ。動的に列名・並び順を変える場合は、ユーザー入力を直接使わずallowlist(許可リスト)で照合してから使う。 - XSSと同じ『データが命令になる』問題。OWASPがXSSとSQLiをまとめて A03 Injection としたのは、根っこが同じだから。この共通構造を理解しておくと応用が利きます。
📊 図解
SQLi 攻撃と防御の対比(シーケンス図)
文字列連結だと SQL の構造が改変され、パラメータ化すると入力は値として扱われます。
図を描画中...