1. 文字列連結というアンチパターン #string-concat
SQLi の根本原因は、サーバ側のコードがこのように書かれていることです:
sql = "SELECT * FROM products WHERE category = '" + userInput + "'" db.execute(sql)
一見、自然な書き方に見えます。userInput が "スキンケア" なら、組み立てられる SQL は:
SELECT * FROM products WHERE category = 'スキンケア'
問題ありません。では、userInput に '(シングルクォート1文字)が入ったら?
SELECT * FROM products WHERE category = ''' ↑↑↑ SQLパーサが混乱、構文エラー
たった1文字の入力でクエリの構造が壊れる——これが「ユーザの入力が SQL の構造そのものを変えられる」状態です。攻撃者はこの裂け目を使って、構造を意図的に書き換えます。
2. 文字列リテラルを途中で閉じる #close-literal
入力に ' を含めることで、文字列リテラルを「攻撃者の好きな位置で」閉じることができます。
例: userInput = "スキンケア' OR 1=1" を入れると、組み立てられる SQL は:
SELECT * FROM products WHERE category = 'スキンケア' OR 1=1' ↑ ↑ ↑ 開く 閉じる 余り
'スキンケア' という文字列リテラルが完成し、その後ろに OR 1=1 という条件が SQL として認識されます。最後に余分な ' が1つ残ってエラーになりますが——これを片付ければ攻撃成立。
3. 閉じた後ろに任意の SQL を書く #append-sql
文字列リテラルが閉じた後ろは「SQL文の続き」として解釈されるので、攻撃者は任意の SQL を書けます。書ける SQL の例:
OR 1=1— 条件を常に真にして全行を返させる(Lab 01)UNION SELECT ...— 別のクエリを連結して結果に紛れ込ませる(Lab 03〜10); DROP TABLE products— 別の SQL 文を実行する(スタッククエリ、Lab 14)- サブクエリ
(SELECT ... FROM ...)で別テーブルから値を引く(Lab 11〜)
どれも「文字列を途中で閉じて、後ろに SQL を書く」という同じ原理から派生しています。
4. SQL のコメント記号 #sql-comments
SQL にはプログラミング言語と同じくコメント記号があります。コメント以降は SQL パーサに無視されます:
--(ハイフン2つ) — 行末までコメント。標準SQL、ほぼ全 DB 共通#— MySQL 固有、行末までコメント/* ... */— 範囲コメント、ほぼ全 DB 共通
攻撃の文脈では、-- が最も使われます。前述の例で残った余分な ' を消すには:
-- 入力: スキンケア' OR 1=1-- SELECT * FROM products WHERE category = 'スキンケア' OR 1=1--' ↑ ここからコメント、無視される
これで構文的に完全な SQL になり、エラーなく実行されます。
-- 注意点
MySQL では -- の後ろにスペースまたは改行が必要です。'-- - のように後ろに何か付けるか、# を使うのが安全。詳しくは Lab 08 で扱います。
5. OR 1=1 — 条件を常に真にする発想 #or-true
OR 演算子は「左右どちらかが真なら全体が真」。1=1 は常に真なので、X OR 1=1 は X が何であろうと常に真になります。
-- 元のクエリ: WHERE category = 'スキンケア' → 「スキンケア」の行だけ -- 注入後: WHERE category = 'スキンケア' OR 1=1 → 全行が条件成立
Lab 01 のように「WHERE category = '...' AND status = '公開'」という構造でも、OR 1=1 を入れれば:
WHERE category = '' OR 1=1 AND status = '公開'
AND は OR より優先されるため、これは category='' OR (1=1 AND status='公開') として評価されます。"公開" の商品全部が返ります。下書きまで見たい場合は -- で AND ごと消す:
WHERE category = '' OR 1=1--' AND status = '公開' ↑ ここから全部コメント
これで status の絞り込みすら無効化され、下書きも含めた全14商品が返されます——Lab 01 の正解パスです。
Lab 02 のログインバイパスも同じ発想です。WHERE member_id='admin' AND password='???' の AND の後半を、'-- でコメント化して消す。条件が WHERE member_id='admin' だけになり、パスワード不要でログイン成立。WHERE の論理を破壊する手口は、SQLi の最も基礎的なパターン。