Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SQLインジェクション ――攻撃の仕組みから対策まで

SQLインジェクション ――攻撃の仕組みから対策まで

Avatar for tanahiro2010

tanahiro2010

July 05, 2026

More Decks by tanahiro2010

Other Decks in Education

Transcript

  1. アジェンダ 1. SQLインジェクションとは 2. なぜ起こるのか(仕組み) 3. 基本編:ログインバイパス 4. 基本編:UNIONベースの攻撃 5.

    発展編:ブラインドSQLインジェクション 6. 発展編:スタック型・セカンドオーダー 7. 対策:プリペアドステートメントほか 8. まとめ SQLインジェクション講座 © 2026 4
  2. 演習の進め方(4ステップ) 各PoCはこの4ステップで進めます。攻撃して終わりにしないのがポイント。 1. 脆弱なコードを読む( app.py )→ どこが危ないか予想する 2. 攻撃してみる →

    ペイロードを入力し、突破できることを確認する 3. 自分で修正する → app.py の該当箇所をプレースホルダに書き換える 4. 再攻撃して防御を確認する → 同じペイロードが効かなくなることを確認する 答え合わせ用に secure_app.py を用意していますが、 まず自分で直してから見るとより理解が深まります SQLインジェクション講座 © 2026 5
  3. OWASP Top 10 での位置づけ 長年 Injection カテゴリの中心的存在 2021年版では A03:2021 –

    Injection に統合 依然として実際のインシデントで多発する脆弱性 「古い脆弱性」ではなく「今も現役の脆弱性」 SQLインジェクション講座 © 2026 7
  4. 2. なぜ起こるのか 文字列結合でSQLを組み立てるコード username = request.form["username"] password = request.form["password"] query

    = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'" cur.execute(query) username や password にSQLの構文として意味を持つ文字( ' や -- など)を 混ぜられると、SQL文の意味が変わってしまう SQLインジェクション講座 © 2026 8
  5. 文字列結合の危険性(図解) 本来のSQL: SELECT * FROM users WHERE username = '[入力]'

    AND password = '[入力]' username に admin' -- を入力すると… SELECT * FROM users WHERE username = 'admin' --' AND password = '...' ↑ ここから後ろはコメント化 → パスワードチェックの部分が丸ごと無視される SQLインジェクション講座 © 2026 9
  6. 3. 基本編:ログインバイパス 典型的なペイロード ユーザー名に入力 効果 admin' -- パスワード条件をコメントアウト admin' #

    MySQLでの同上( # がコメント) ' OR '1'='1' -- 常に真になる条件を追加 '1'='1' は常に真 → WHERE 条件全体が真になり、最初のレコードでログイン成立 SQLインジェクション講座 © 2026 10
  7. PoC①:ログインバイパスをやってみよう フォルダ: 01_login_bypass/ cd 01_login_bypass && pip install -r requirements.txt

    && python app.py # http://127.0.0.1:5001 1. app.py の文字列結合部分を確認する 2. ユーザー名 admin' -- /パスワード空欄でログイン →突破できることを確認 3. app.py を ? プレースホルダに書き換えてみる 4. 同じ入力でログインが失敗することを確認(答え合わせ: secure_app.py ) SQLインジェクション講座 © 2026 11
  8. PoC①で理解してほしいこと 文字列結合だと、入力に含まれる ' がSQLの構文の一部として解釈されてしまう → -- や OR '1'='1' で条件を自由に書き換えられる

    プレースホルダ( ? )を使うと、入力値は常に「単なる値」として渡される → ' や -- が入っていても構文としては解釈されない この「文字列結合 → プレースホルダ」というパターンは、以降のPoCでも繰り返し出てくる SQLインジェクション講座 © 2026 12
  9. 4. 基本編:UNIONベースの攻撃 UNION SELECT とは 2つの SELECT の結果を縦に結合するSQL構文。 検索結果として表示される画面に、別のテーブルの内容を紛れ込ませることができる。 攻撃の手順

    1. ORDER BY などでカラム数を特定する 2. UNION SELECT で列数・型を一致させる 3. 抜き出したいテーブル(例: users )を指定する SQLインジェクション講座 © 2026 13
  10. UNION攻撃の実例 脆弱な検索クエリ(商品名検索、3カラムを表示) : SELECT id, name, price FROM products WHERE

    name LIKE '%[入力]%' 攻撃者の入力: %' UNION SELECT id, username, password FROM users -- → 商品一覧の中にユーザー名とパスワードが混ざって表示される SQLインジェクション講座 © 2026 14
  11. PoC②:UNIONインジェクションをやってみよう フォルダ: 02_union_based/ cd 02_union_based && pip install -r requirements.txt

    && python app.py # http://127.0.0.1:5002 1. app.py の検索( LIKE )部分の文字列結合を確認する 2. %' UNION SELECT id, username, password FROM users -- で users テーブルを抽出 →突破確認 3. app.py をプレースホルダに書き換えてみる 4. 同じペイロードでヒット件数が0件になることを確認(答え合わせ: secure_app.py ) SQLインジェクション講座 © 2026 15
  12. エラーベースの補助テクニック DBがエラーメッセージをそのまま画面に返す設計だと、 エラー文からテーブル名・カラム名・DB種別が漏れる 例: ' AND 1=CONVERT(int, (SELECT TOP 1

    table_name FROM information_schema.tables)) -- カラム数の特定にも ORDER BY 1,2,3... を使ってエラーの有無で判定できる 本番環境で詳細なDBエラーを表示するのはそれ自体が情報漏洩 SQLインジェクション講座 © 2026 17
  13. Boolean-based Blind の例 正常な注文照会: SELECT * FROM orders WHERE id

    = [入力] 真になる入力 → 結果が表示される: 1 AND 1=1 偽になる入力 → 結果が表示されない: 1 AND 1=2 この「表示/非表示」の差分を1文字ずつ繰り返すことで、 SUBSTR() などと組み合わせてDBの内容を1文字ずつ確定させられる SQLインジェクション講座 © 2026 19
  14. Time-based Blind の例 画面に差が出ない場合は、応答時間の差を使う 1 AND (CASE WHEN (SUBSTR((SELECT password

    FROM users LIMIT 1),1,1)='a') THEN SLEEP(5) ELSE 0 END) 条件が真 → 5秒待たされる 条件が偽 → 即座に応答が返る → 画面に何も表示されなくても、時間差だけで真偽が分かる SQLインジェクション講座 © 2026 20
  15. PoC③:ブラインドSQLインジェクションをやってみよう フォルダ: 03_blind_injection/ cd 03_blind_injection && pip install -r requirements.txt

    && python app.py # http://127.0.0.1:5003 1. app.py の数値パラメータの文字列結合部分を確認する 2. /track?order_id= に Boolean-based / Time-based の両方を試す →突破確認 3. app.py を int() チェック+プレースホルダに書き換えてみる 4. 同じペイロードがエラーになり実行されないことを確認(答え合わせ: secure_app.py ) SQLインジェクション講座 © 2026 21
  16. 6. 発展編:スタック型SQLインジェクション Stacked Queries とは 1回のリクエストで複数のSQL文をセミコロン区切りで連続実行させる攻撃 note') ; DROP TABLE

    notes; -- SELECT だけでなく INSERT / UPDATE / DELETE / DROP まで実行できてしまう ドライバや設定によって複数文実行が許可されている場合に成立する SQLインジェクション講座 © 2026 23
  17. PoC④:スタック型 & セカンドオーダーをやってみよう フォルダ: 04_advanced_second_order/ cd 04_advanced_second_order && pip install

    -r requirements.txt && python app.py # http://127.0.0.1:5004 1. app.py の executescript() と /admin/report の文字列結合部分を確認する 2. /note でスタック型、 /register → /admin/report でセカンドオーダーを試す →突破確認 3. app.py を execute() +プレースホルダに書き換えてみる(脆弱ポイントは2箇所) 4. 同じ手順で攻撃が通らなくなることを確認(答え合わせ: secure_app.py ) SQLインジェクション講座 © 2026 25
  18. PoC④で理解してほしいこと 複数文を連続実行できる実装( executescript() 等)は、 INSERT だけを想定していても DROP / UPDATE まで実行されてしまう

    「すでにDBに保存された値だから安全」という思い込みが最も危険 → 対策は「入力を受け取る場所」だけでなく値を使うすべての場所に必要 ここまでの4つのPoCは形は違うが、根本原因はすべて同じ「文字列結合」 SQLインジェクション講座 © 2026 26
  19. 7. 対策①:プレースホルダ(プリペアドステートメント) 最も重要かつ最も効果的な対策 # 脆弱 query = f"SELECT * FROM

    users WHERE username = '{username}'" cur.execute(query) # 安全 cur.execute("SELECT * FROM users WHERE username = ?", (username,)) 値はSQL構文とは別チャンネルでDBに渡される 入力に ' や -- が入っていても単なる文字列として扱われる SQLインジェクション講座 © 2026 27
  20. 対策④:多層防御(Defense in Depth) 層 対策 コード プレースホルダ / ORM DB

    最小権限アカウント、ストアドプロシージャ ネットワーク WAF による既知パターンの検知・遮断 運用 ログ監視、依存ライブラリの更新、定期的な脆弱性診断 「1つ対策すれば終わり」ではなく、複数の層を重ねる SQLインジェクション講座 © 2026 30
  21. まとめ フェーズ 手法 対策 基本 ログインバイパス / UNION プレースホルダ 発展

    ブラインド(Boolean/Time) 詳細エラー非表示・最小権限 発展 スタック型・セカンドオーダー 使う場所すべてでプレースホルダ 全体 — 多層防御・入力値検証・WAF・監視 SQLインジェクションは古典的だが今も現役の脆弱性。 「どこかで文字列結合していないか」を常に疑う姿勢が最大の防御。 SQLインジェクション講座 © 2026 31
  22. 参考リンク OWASP SQL Injection OWASP SQL Injection Prevention Cheat Sheet

    PortSwigger Web Security Academy - SQL injection SQLインジェクション講座 © 2026 32