【テクニカル・上級編】Trusted Types APIによるDOMベースXSSの根本的根絶 – アプリケーションセキュリティ & 安全な開発防御ガイド

DOMベースXSSの「最後の聖域」:Trusted Typesによる根本的根絶の実践

DOMベースのXSSは、もはや「単なるコードの書き間違い」というレベルを超え、モダンなフロントエンドアーキテクチャの根幹を揺るがす脅威だ。ReactやVueといったフレームワークが自動エスケープを提供していても、我々が日々直面するインシデントでは、dangerouslySetInnerHTML、サードパーティ製ライブラリの脆弱性、あるいはSPAルーティングの不備を突いたDOMシンクの汚染が後を絶たない。

多くのエンジニアは、依然として「サニタイズ(DOMPurify等)」を盾にしているが、それはあくまで「後付けのパッチ」に過ぎない。今回取り上げるTrusted Types APIは、これまでの「文字列として扱うDOM操作」という設計上の欠陥を根底から覆し、ブラウザエンジンレベルで危険なシンクを封鎖する強力な武器だ。

—

1. なぜ「サニタイズ」だけでは限界があるのか

現代のWebアプリケーションにおいて、データと実行コードの境界線は曖昧だ。element.innerHTML = userInput という一行は、プロトコルレベルでのデータ通信を即座にJavaScriptの実行コンテキストへと昇華させる。

攻撃者の視点に立てば、innerHTMLやeval()のようなDOMシンクは、メモリ上に配置された任意のJavaScriptコードを「正当なスクリプト」として解釈させるための「入り口」だ。どれだけ強力なブラックリストベースのサニタイズを施しても、パーサーの挙動の差異や、ブラウザごとのパースエンジンのバグ(いわゆるMutation XSS)によって、防御策は常に追いかける側になる。

Trusted Typesは、このゲームのルールを書き換える。「文字列を直接DOMシンクに渡すことを禁止し、特定の型(Trusted Types)のみを許可する」という強制的な型制約をランタイムに導入するのだ。

—

2. Trusted Typesのアーキテクチャ:Policyによる防衛線

Trusted Typesの導入は、単なるコード修正ではなく、アプリケーションの「DOM操作に関するセキュリティポリシーの宣言」である。

以下のコードは、アプリケーション内で許可される「安全な変換」を定義する実装例だ。

// セキュリティポリシーの定義: “my-app-policy” という名前で作成
const policy = trustedTypes.createPolicy(‘my-app-policy’, {
createHTML: (input) => {
// ここでDOMPurifyなどの信頼できるライブラリを通す
// 文字列ではなく、TrustedHTMLオブジェクトを返すことが重要
return DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: true });
},
createScriptURL: (input) => {
// スクリプト読み込み元のドメインを厳格にホワイトリスト化する
if (input.startsWith(‘https://cdn.trusted-assets.com/’)) {
return input;
}
throw new TypeError(‘不正なスクリプトソースです’);
}
});

// 以降、直接文字列を代入しようとするとブラウザが例外を投げる
// element.innerHTML = “

…

“; // これでJS実行がブロックされる
element.innerHTML = policy.createHTML(userInput); // これのみが許可される

このアーキテクチャの真価は、element.innerHTML への直接代入をブラウザエンジンが型エラーとして弾く点にある。開発者がいくら注意深くコードを書いても、ヒューマンエラーは避けられない。しかし、ブラウザという「物理的な制約」を導入することで、脆弱性をコードベースから物理的に排除できる。

—

3. CSPによる強制適用:インシデントハンドリングの自動化

Trusted Typesを開発環境だけで終わらせず、本番環境で強制適用するには、Content Security Policy (CSP) の require-trusted-types-for ディレクティブを利用する。

Content-Security-Policy: require-trusted-types-for ‘script’; trusted-types my-app-policy;

  • require-trusted-types-for 'script': 文字列入力を受け付ける全てのDOMシンクに対して、Trusted Typesの使用を義務付ける。
  • trusted-types my-app-policy: 許可するポリシー名を指定。これ以外のポリシーや、ポリシー未定義の文字列代入はすべてブラウザによってブロックされる。

これにより、既存のライブラリが古いDOM操作を行っている場合、即座にコンソールにエラーが吐き出され、インシデントの予兆を開発チームが検知できる。これは「事後のスキャン」ではなく、「設計時点でのバグの封じ込め」である。

—

4. 最高峰のセキュリティアーキテクトが説く「死角」

Trusted Typesは強力だが、銀の弾丸ではない。以下の観点を忘れてはならない。

1. フレームワークの追従: ReactやAngularなどのモダンフレームワークは、Trusted Typesへの対応を進めている。しかし、サードパーティ製ライブラリ(特に古いjQueryプラグインや、複雑なDOM manipulationを行うライブラリ)は、このポリシーと衝突して動作しなくなる可能性がある。移行には「段階的な導入(Reporting Onlyモード)」が必須だ。
2. ビジネスロジックの脆弱性: DOMベースXSSを防いでも、JSONPのエンドポイントや、APIレスポンスの誤ったハンドリングによるロジック上の脆弱性は残る。Trusted Typesはあくまで「DOM汚染」という特定のベクタを遮断する層であり、アプリケーション全体の認証・認可の欠陥を埋めるものではない。
3. 生成AIによるコード生成: 近年、AIが生成したコードをそのままプロダクションに反映するケースが増えている。AIは「動くコード」を作ることは得意だが、「Trusted Typesを考慮したセキュアなコード」を生成できるとは限らない。AIが生成したUIコンポーネントこそ、Trusted Typesによるガードレイルで囲い込むべき対象だ。

結論

XSSは、もはや「防ぐのが難しい問題」ではない。ブラウザが提供する型安全性を活用し、DOM操作を厳格に管理するポリシーをコードベースに組み込むこと。これが、これからのWeb開発におけるセキュリティの最低ラインだ。

泥臭いパッチ当てに時間を浪費するのはもう終わりにしよう。APIを定義し、ポリシーを適用し、システム全体を「安全であることが数学的に保証された状態」へと近づける。それが我々、セキュリティアーキテクトの仕事だ。

コメント

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