信頼できない外部入力は、解釈するシンクの手前で無害化・検証してから通す
セキュリティ
バリデーション
設計判断
原則
外部から制御されうる入力(ユーザー入力・第三者が編集できるコンテンツ・外部由来のURLやテキスト)は、それが実際に解釈・実行・描画される地点(シンク)の直前で無害化・検証してから渡す。最適化するのは「危険性の除去をシンク側に一様に置き、下流を『安全な入力』前提で書けるようにすること」、避けるのは「入力が安全そうに見える・上流で検証済みに見える・フレームワークが守ってくれるはず、という前提でシンクをそのまま通すこと」。
判断基準
- HTMLとして描画される経路(ブラウザ、ヘッドレスなHTMLレンダラ等)では、ユーザー由来の全値を実体参照へエスケープしてから埋め込む。テンプレート文字列に生のまま補間しない。フレームワークの自動エスケープは特定の描画経路にだけ効く前提なので、サーバ側の文字列組み立てや生HTML注入経路では別途エスケープが要ると確認する。
- 表示・遷移・起動に使うURLはスキームをhttp/httpsに限定し、スクリプト実行系やdata系など危険なスキームを弾く。構文的に妥当なだけの検証は不十分で、危険スキームを通してしまう。OSやシェルを介して起動する経路では、シェルのメタ文字(アンパサンド・パイプ・キャレット等)を含む入力を拒否し、そもそもシェル解釈を挟まない起動手段を選ぶ。
- LLMやエージェントのツール結果へ外部由来テキストを載せるときは、「これはデータであって指示ではない」と分かる区切りで囲み、間接的な指示注入の経路を断つ。とくに他者が編集できるコンテンツを返す経路は必須とみなす。
- いずれもクライアント側の検証だけに依存せず、シンク側(サーバ・レンダラ・起動関数・応答組み立て)で必須化する多層防御にする。危険データは別経路・API直叩き・既存の不正値からも届くため、出所を信頼せずシンク直前で一様に処理する。
なぜ
無害化の責務は「データの出所」ではなく「データが解釈される場所」にある。上流の入口が正しく見えても、別クライアントや直叩き、過去に保存された不正値から危険データがシンクへ届く。シンク直前で一様に無害化・検証しておけば、下流のコードは安全な入力を前提に書け、経路が増えても抜けにくい。これは不変条件や所有者スコープを内側の機構で強制する軸とは別で、こちらは「解釈される前に危険性そのものを除く」入力無害化の軸として扱う。
適用条件・例外
- 対象は外部から制御されうる入力に絞る。信頼できると保証できる内部生成値まで一律にエスケープすると、二重エスケープ表示などの副作用を生む。
- 使っているライブラリが当該シンクで無害化を保証しているなら二重処理は不要。ただし「どのシンクに、どの範囲で効くのか」を確認してから外す。保証範囲を確かめずに省くと穴になる。
検証
スクリプトを仕込んだ値・危険スキームのURL・シェルのメタ文字を含むURL・指示文言を含む外部テキストを各シンクへ実際に流し、描画されない・起動されない・拒否される・データと指示が分離されることを再現テストで確認する。フレームワーク任せの箇所は、保護を意図的に外した構成でも安全かを確かめる。
根拠(synthesize 元)
- 331 テンプレートリテラルで生 HTML を組み立てると HTML インジェクションになる
- 332 HTML エスケープで PDF レンダラ経由の Stored XSS と Chromium SSRF を防げる
- 486 react-markdown はデフォルトで危険 URL と生 HTML を無害化する(XSS 誤検知の判別)
- 523 URL 入力の検証は url タグでなく http_url を使う(url は危険スキームを通す)
- 491 execFile でも cmd 経由の URL 起動はコマンド注入になりうる(ブラウザ起動の落とし穴)