【テクニカル・上級編】DOMベースXSSの発生メカニズムとクライアントサイドのソース・シンク関係 – アプリケーションセキュリティ & 安全な開発防御ガイド

DOMベースXSS:クライアントサイドの「盲点」を突く深淵なるエントロピー

多くのエンジニアが「XSSはサーバー側のバリデーションで防げる」と信じている。だが、それはかつての時代の遺物だ。SPA(Single Page Application)全盛の現代、攻撃者はサーバーのログを汚すことなく、ブラウザというサンドボックスの「内側」で完結する致命的なコード実行を狙っている。

DOMベースXSSの恐ろしさは、サーバー側のWAFやIDSが完全に無力であるという点にある。通信は発生しない。すべてはブラウザのメモリ上、クライアントサイドのJavaScriptエンジン内で完結する。

1. メカニズムの正体:ソースとシンクの「汚染」

DOMベースXSSの根本原因は、「制御不能な入力(Source)」が「危険な実行関数(Sink)」へ流れ込むパイプラインの設計ミスだ。

  • Source(ソース): location.hash や location.search、document.referrer、あるいは postMessage のイベントデータ。これらはユーザー(または外部コンテキスト)が改ざん可能な「エントロピーの混入点」だ。
  • Sink(シンク): innerHTML、outerHTML、document.write()、あるいは eval() や setTimeout() に渡される文字列。

攻撃者が # のようなフラグメントを仕込んだとき、アプリケーションがその値を適切にサニタイズせず innerHTML に代入すれば、ブラウザのHTMLパーサーはそれを「信頼されたDOMノード」として解釈する。ここで発生するのは単なる文字列操作ではなく、メモリ空間上での論理的な権限昇格だ。

2. コードで見る「脆弱なアーキテクチャ」と「防御の境界線」

現場のコードレビューでよく見る、最も危険な実装例を見てほしい。

// 脆弱な実装例:URLのハッシュ値をそのままDOMに反映する
const hash = window.location.hash.substring(1);
const container = document.getElementById(‘content’);

// 警告:innerHTMLはHTMLパーサーを起動させる。
// 攻撃者はここで任意のスクリプトを注入できる。
container.innerHTML = decodeURIComponent(hash);

これを防ぐための現代的なアーキテクチャは、「DOMの構築」と「データ」を完全に分離することに尽きる。

// セキュアな実装例:テキストコンテンツとしてのみ扱う
const hash = window.location.hash.substring(1);
const container = document.getElementById(‘content’);

// textContentを使用することで、HTMLパーサーを介さず「単なる文字列」としてレンダリングする
// これにより、たとえ悪意のあるタグが含まれていても、ブラウザはそれを実行せず文字として表示する
container.textContent = decodeURIComponent(hash);

3. 高度な防衛:信頼の連鎖を断ち切る「Trusted Types」

しかし、大規模なアプリケーションで全ての innerHTML を追跡するのは現実的ではない。そこで導入すべきが、Trusted Types APIだ。これは、危険なシンクに渡す値を「事前にポリシーで定義した型」に強制変換させる仕組みである。

// セキュリティポリシーの定義(CSPと連動)
const policy = trustedTypes.createPolicy(‘myPolicy’, {
createHTML: (input) => {
// ここでDOMPurify等を使って強力にサニタイズする
return DOMPurify.sanitize(input);
}
});

// 以降、文字列を直接innerHTMLに代入しようとするとブラウザがエラーを投げる
// 必ずpolicyを通す必要がある
element.innerHTML = policy.createHTML(userInput);

4. チーフホワイトハッカーとしての警鐘:プロンプトインジェクションとの対比

今、我々が対峙している生成AIのプロンプトインジェクションも、実はこのDOMベースXSSの延長線上にある。

AIが生成したテキストをそのままUIに表示する際、モデルが吐き出した「隠れプロンプト」や「HTMLタグ」が、Webアプリケーションのコンテキストで実行されてしまうリスクだ。DOMベースXSSの対策として確立された「レンダリング前の厳格な型変換」と「エスケープの強制」は、AIアプリケーションのガードレイル設計においても、最も基本的かつ強力な武器となる。

結論:境界防衛の終焉と「内部」の堅牢化

もはや「境界」という概念は崩壊している。WAFは、攻撃者がブラウザのURLバーを操作して発生させるこの攻撃を止めることはできない。

セキュリティアーキテクトが注力すべきは、「ブラウザの中で動くコード自体が、外部からの入力にどう対峙するか」という、フロントエンドの防御力学だ。コードレビューの際、innerHTML という文字列を見つけたら、即座に「それは信頼できるソースから来ているか?」と問い直してほしい。

脆弱性は常に、設計者の「想定外のルート」に潜んでいる。あなたのコードの「sink」は、本当に安全か? 一度、徹底的にプロファイリングしてみることを強く推奨する。それが、インシデントハンドリングの現場で泥をかぶる前の、唯一の備えだからだ。

コメント

タイトルとURLをコピーしました