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

DOM-based XSSの深淵:ブラウザの実行コンテキストを支配する「ソースとシンク」の静的解析

セキュリティ界隈では、DOM-based XSSは「枯れた脆弱性」として軽視されがちだ。しかし、現代のシングルページアプリケーション(SPA)や、マイクロフロントエンドアーキテクチャにおいて、この脆弱性は依然として「特権昇格」や「セッションハイジャック」の最短ルートであり続けている。

サーバーサイドのコードをどれほど堅牢に書き上げても、ブラウザというサンドボックスの内部で、JavaScriptの実行フローが汚染されていれば、それは無意味だ。今日は、表面的なバリデーションではなく、ブラウザの実行モデルそのものを制御するアーキテクトの視点で、この問題の本質を解剖する。

—

1. 脆弱性の発生メカニズム:ソースからシンクへの「汚染された伝搬」

DOM-based XSSを理解するために重要なのは、ブラウザのメモリ上で行われるデータの「汚染伝搬(Taint Propagation)」だ。

  • Source(汚染源): 攻撃者が制御可能なデータがブラウザ環境に入る地点(location.hash, location.search, document.referrer, window.name など)。
  • Sink(実行地点): データを実行・レンダリングする地点(innerHTML, eval(), setTimeout(), document.write() など)。

問題の本質は、「開発者が、クライアントサイドで完結するデータフローが外部入力に依存しているという事実を、サーバーサイドのプロトコル境界と混同していること」にある。

例えば、ReactやVueなどのモダンフレームワークを使っていても、dangerouslySetInnerHTML を安易に使用したり、サードパーティのライブラリがURLパラメータを適切にエスケープせずDOMに挿入したりするだけで、セキュリティの防御層は一瞬で崩壊する。

—

2. 実践的監査:シンクの追跡と防御のアーキテクチャ

コードレビューにおいて、すべてのinnerHTMLを追いかけるのは非効率だ。我々アーキテクトがやるべきは、「ソースを特定し、その入力を無害化するまでのパイプラインを強制すること」である。

安全な実装パターン:DomPurifyによる無害化

直接DOMを操作するのではなく、一度「サニタイズ(浄化)」という中間層を必ず通す設計にする必要がある。

import DOMPurify from ‘dompurify’;

// 攻撃者が制御するURLフラグメントを取得
const userControlledInput = window.location.hash.substring(1);

// 【重要】直接シンクへ渡さず、必ずサニタイザーを通す
// 攻撃的なタグやイベントハンドラ(onerror等)を構造的に除去する
const cleanHTML = DOMPurify.sanitize(userControlledInput);

// 安全にレンダリング
document.getElementById(‘content’).innerHTML = cleanHTML;

—

3. 防御の多層化:CSPによる実行環境の制約

コードレベルの修正だけでは、将来的な脆弱性の混入を防ぎきれない。インフラ・フロントエンド設計の両面から「ガードレイル」を敷くのが、我々プロの仕事だ。

Content Security Policy (CSP) の適用

DOM-based XSSの究極的な防御策は、インラインスクリプトの実行を物理的に禁止することだ。

HTTPヘッダーでCSPを設定し、実行コンテキストを制約する
Content-Security-Policy: default-src ‘self’; script-src ‘self’ ‘nonce-random123’; object-src ‘none’; base-uri ‘self’;

このポリシーを適用すれば、たとえ攻撃者がinnerHTMLを通じて

securityintronationalをフォローする

コメント

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