SQLインジェクション — 入力からDBに不正な命令を送り込む

中級の解説は準備中のため、中級の内容を表示しています。

概要 — まず全体をつかむ

初級では「データのはずの入力が命令になってしまう」本質を見ました。中級では、なぜ混ざるのかと、どの層で断つかを整理します(守り全体は姉妹ユニット Webのセキュリティ(全体像))。

詳細 — 1段階ずつ追う

どう起きる?

原因は一点です。入力を文字列連結で SQL に混ぜると、入力の中身が「命令の一部」として解釈されてしまう。

DB にとって届いた SQL は一続きの文字列です。命令の骨組みとデータの境目は、組み立てた側の意図でしかありません。連結でつなぐと、入力に混ぜた命令の切れ端が骨組みを書き換え、全件を抜く・消す・ログインを突破する、といったことが起きます。

アニメーション『SQLインジェクション(命令とデータの分離)』を開く
⚠ 危険:入力をそのまま連結入力欄(名前など)' OR '1'='1連結組み立てられたSQL(入力が命令の一部に化ける)SELECT * FROM usersWHERE name = '' OR '1'='1'結果:条件が常に真認証回避・全件取得ができてしまう入力(データのはず)が「命令」として解釈されてしまったWHERE の条件が「name='' または 1=1」となり、1=1 は常に成立 → 全行が対象に✓ 安全:パラメータ化(プレースホルダ)入力欄(名前など)' OR '1'='1値として渡す命令の骨組みは先に固定・入力は別枠(?)へSELECT * FROM usersWHERE name = ? ← 入力はここへ“データ”として結果:ただの検索語入力は命令に化けない(該当なしで安全)入力は必ず「値(データ)」として扱われ、命令として動かない入力全体が name の検索語(= "' OR '1'='1" という名前を探す)になるだけ要:命令(SQL)とデータ(入力)を分離する

登場人物メモ

  • ※1 命令とデータの分離 — SQL文の骨組みと、入力値を別々にDBへ渡す考え方
  • ※2 最小権限 — アプリのDBユーザーに必要以上の権限を与えないこと

どう防ぐ?

  1. パラメータ化クエリ(プレースホルダ) — 命令の骨組みを先に固定し、入力は「値(データ)」として別枠で渡す。命令とデータが分離されるので入力は命令として動かない。これが王道
  2. 文字列連結で SQL を組み立てない — これが鉄則。自前のエスケープは抜けやすい
  3. ORMの適切な利用 — ORM も内部でパラメータ化を使うが、生SQLを組み立てる箇所は同じリスクなので油断しない
  4. DBユーザーの最小権限 — アプリ用ユーザーに削除・全件取得などの過剰な権限を与えない。破られても被害を絞る
  5. 識別子は許可リストで — 表名・列名・並び順(ORDER BY)はプレースホルダで「値」として渡せない。動的に変えるなら、固定の許可リストから選ぶ(入力をそのまま使わない)
  6. エラー詳細を返さない — DBのエラーをそのまま画面に出すと、テーブル構造などのヒントを与える
  7. 入力検証 — 補助として型や形式も確かめる

⚠️ 気をつける

  • エスケープ自作に頼る — 自前の文字置換は抜け穴が残りやすい。パラメータ化が正解
  • エラーメッセージの露出 — 詳細を隠しても、応答の真偽や時間差から探るブラインドな手口がある。だからエラー非表示は補助
  • ORMでも油断しない — 生SQLや動的な条件組み立ての箇所は同じリスク
  • 種類を知る — 結果に差が出るエラーベース、差が見えにくいブラインド(真偽・時間差)など手口は複数あるが、対策の根本は同じ

理解度チェック

そのまま解けます(成績は保存されません)。無料アカウントを作ると、学習の記録と進捗の山登りが始まります。

1. ブラインドSQLインジェクションの説明として正しいのは?

2. パラメータ化クエリ(プレースホルダ)が有効なのはなぜか?

3. 入力が「命令」として解釈されてしまう根本原因はどれ?

4. 表名・列名や並び順(ORDER BY)を入力に応じて動的に変えたい。安全な方法はどれ?

5. 自前のエスケープ(危険な記号の文字置換)に頼るのが良くない理由は?

6. 「DBの詳細エラーを画面に出さない」対策の位置づけとして正しいのは?

7. 表名・列名や並び順を動的に変えるとき 入力を直接使わず あらかじめ用意した安全な選択肢から選ぶ方式を「○○リスト」と呼ぶ。○○に入る語(漢字2文字)は?

8. アプリ用のDBユーザーに 必要以上の権限を与えないことを漢字4文字で何という?