🎓 レベル:標準 | 重要度:A(必須) 📎 前提:OWASP Top 10 の概観 ⚠️ 本トピックは防御の実装のみを扱います。攻撃ペイロードの作り方は記載しません。
要点(BLUF)
- インジェクションは「信頼できない入力が、データではなくコード/命令として解釈される」ときに起きる。根本原因はデータとコードの混在。
- 防御の本筋はデータとコードの分離:SQL ならパラメータ化(プリペアドステートメント)、HTML 出力なら文脈に応じた出力エンコード。
- 入力検証は許可リスト方式で補強する。ただし入力検証は「補助」、分離が「本丸」。
概念:なぜインジェクションが起きるか
多くの言語・DB・テンプレートは、文字列を受け取ってその場で命令として解釈します。ここに利用者の入力を文字列連結でそのまま混ぜると、入力の一部が命令として実行されてしまう――これがインジェクションの本質です。SQL・OS コマンド・LDAP・テンプレート・HTML(XSS)など、解釈器がある所すべてで起こりえます。
つまり問題は「危険な入力」そのものより、入力(データ)と命令(コード)を混ぜてしまう作りにあります。だから防御も「混ぜない設計」に向かいます。
仕組み:データとコードを分離する
SQL インジェクション → パラメータ化クエリ
文字列連結でクエリを組み立てるのをやめ、プレースホルダを使って値を「データとして」渡します。DB は命令の骨組みを先に確定し、後から来る値を決して命令として解釈しません。
# 安全:パラメータ化(プレースホルダ)。値は常にデータとして扱われる
cur.execute("SELECT * FROM users WHERE email = ?", (user_input,))
# 危険な書き方(文字列連結)はしない。入力が命令に化けうる
# cur.execute("SELECT * FROM users WHERE email = '" + user_input + "'") # NG
ORM を使う場合も、生 SQL に文字列連結で値を差し込まないのが鉄則です。
XSS → 文脈に応じた出力エンコード
HTML に値を出すとき、< > & " などをその文脈の特殊文字としてエスケープすれば、入力はタグやスクリプトではなくただの文字として表示されます。
# 安全:HTMLエスケープしてから出力(テンプレートエンジンの自動エスケープを使う)
import html
safe = html.escape(user_input) # < > & " ' が無害な実体参照になる
現代のテンプレートエンジン(Jinja2 等)は既定で自動エスケープします。自動エスケープを切らないこと、出力先(HTML 本文・属性・JS・URL)ごとに適切なエンコードを選ぶことが要点です。
OS コマンド → API/配列引数で実行
シェルに文字列を渡さず、引数を配列で渡す API を使い、シェル解釈を経由させません。そもそも外部コマンド実行を避けられないかをまず検討します。
図解:分離があれば入力は命令にならない
flowchart LR
IN["信頼できない入力"] --> SEP{"データとコードを分離しているか"}
SEP -->|"はい:パラメータ化・出力エンコード"| SAFE["入力はデータとして扱われ無害"]
SEP -->|"いいえ:文字列連結で混在"| RISK["入力が命令として解釈されうる"]
仕組み:入力検証は「許可リスト」で補強
分離が本丸ですが、入力検証も多層防御として併用します。ポイントは許可リスト(allowlist)――「許す形を定義し、それ以外を弾く」方式です。「危険そうな文字を消す(拒否リスト)」は漏れが出やすく脆い。
- 型・長さ・範囲・形式(メール・日付など)を期待する仕様で検証する。
- 検証はサーバ側で必ず行う(クライアント側検証は UX 用で、防御にはならない)。
- 正規化してから検証する(エンコードの揺れで検証をすり抜けさせない)。
防御側の使い方/設定
- SQL は必ずパラメータ化:文字列連結を禁止。コードレビューと静的解析(セキュアコーディングとレビュー)で機械的に検出。
- 出力は文脈別にエンコード:テンプレートの自動エスケープを有効に保つ。
- 許可リスト検証+正規化:期待する形だけ通す。サーバ側で。
- 最小権限の DB アカウント:アプリ用 DB ユーザーに不要な権限を与えない(万一の被害を限定。最小権限=セキュリティ設計原則)。
- CSP などの多層:XSS は出力エンコードを本丸に、Content-Security-Policy で被害を緩和(認証とセッションの安全な実装)。
なぜ安全か:解釈器に「これはデータ」と明示するから
パラメータ化が安全なのは、命令の構造を先に確定し、値を後から「データ」として渡すため、値がどんな内容でも命令の構造を変えられないからです。出力エンコードが安全なのは、特殊文字を表示用の文字に変換し、ブラウザがそれをタグやスクリプトとして解釈しなくなるからです。いずれも「危険な入力を当てっこする」のではなく、そもそも入力を命令として解釈させないので、未知の手口にも強い。
仕組みの直観
インジェクションは口述筆記の混乱に似ています。秘書(解釈器)に手紙を口述しているとき、文中の「以上、署名して投函」を指示と取られたら事故です。パラメータ化は「ここからここまでは**本文(データ)**です」と封筒を分けて渡すこと。出力エンコードは、表示の際に記号を「ただの文字」として印字し、命令と読み違えさせないことです。
⚠️ よくある誤解・設定ミス
- 文字列連結でクエリを組む:最大の原因。例外なくパラメータ化する。
- 拒否リスト(ブラックリスト)で防ごうとする:漏れる。許可リスト+分離が本筋。
- クライアント側検証だけ:簡単に回避される。サーバ側で必ず検証。
- テンプレートの自動エスケープを切る:XSS を自ら招く。文脈別エンコードを保つ。
- 「入力検証さえすれば分離は不要」と思う:逆。分離が本丸、検証は補助。
対応 lab
攻撃ペイロードを伴うため実行 lab は置きません(防御・教育スコープ)。上記の安全な書き方(パラメータ化・出力エンコード)をコード断片で示すに留めます。
関連
- 弱点の地図での位置 → OWASP Top 10 の概観
- 認証/セッション側の実装防御 → 認証とセッションの安全な実装
- レビュー・静的解析で支える → セキュアコーディングとレビュー