Mímisbrunnr知恵の泉

← サイバーセキュリティ 一覧

🎓 レベル:標準 | 重要度:A(必須) 📎 前提:OWASP Top 10 の概観 ⚠️ 本トピックは防御の実装のみを扱います。攻撃ペイロードの作り方は記載しません。

要点(BLUF)

概念:なぜインジェクションが起きるか

多くの言語・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)――「許す形を定義し、それ以外を弾く」方式です。「危険そうな文字を消す(拒否リスト)」は漏れが出やすく脆い。

防御側の使い方/設定

なぜ安全か:解釈器に「これはデータ」と明示するから

パラメータ化が安全なのは、命令の構造を先に確定し、値を後から「データ」として渡すため、値がどんな内容でも命令の構造を変えられないからです。出力エンコードが安全なのは、特殊文字を表示用の文字に変換し、ブラウザがそれをタグやスクリプトとして解釈しなくなるからです。いずれも「危険な入力を当てっこする」のではなく、そもそも入力を命令として解釈させないので、未知の手口にも強い。

仕組みの直観

インジェクションは口述筆記の混乱に似ています。秘書(解釈器)に手紙を口述しているとき、文中の「以上、署名して投函」を指示と取られたら事故です。パラメータ化は「ここからここまでは**本文(データ)**です」と封筒を分けて渡すこと。出力エンコードは、表示の際に記号を「ただの文字」として印字し、命令と読み違えさせないことです。

⚠️ よくある誤解・設定ミス

対応 lab

攻撃ペイロードを伴うため実行 lab は置きません(防御・教育スコープ)。上記の安全な書き方(パラメータ化・出力エンコード)をコード断片で示すに留めます。

関連