utsuroi sqli labs 知識ベース 02 / 05 — インジェクションの原理
Injection Principle · Knowledge Base
02

インジェクションの原理

The string-concat anti-pattern at the heart of SQLi

SQL injection はテクニックの集合ではなく、たった一つの構造的問題から発生する一族です。「ユーザの入力を SQL 文字列に直接連結する」という実装パターン——これさえ理解すれば、すべての攻撃手法が同じ原理の応用と分かります。

1. 文字列連結というアンチパターン #string-concat

「+」で SQL を組み立てた瞬間、扉が開く。

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 の例:

どれも「文字列を途中で閉じて、後ろに SQL を書く」という同じ原理から派生しています。

4. SQL のコメント記号 #sql-comments

後ろの不要な部分を「無いことに」する道具。

SQL にはプログラミング言語と同じくコメント記号があります。コメント以降は SQL パーサに無視されます:

攻撃の文脈では、-- が最も使われます。前述の例で残った余分な ' を消すには:

-- 入力: スキンケア' OR 1=1--
SELECT * FROM products WHERE category = 'スキンケア' OR 1=1--'
                                                       ↑
                                              ここからコメント、無視される

これで構文的に完全な SQL になり、エラーなく実行されます。

MySQL の -- 注意点

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 の最も基礎的なパターン。