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

DOM型XSSを「過去の遺物」にする:Trusted Types API完全攻略ガイド

現場でインシデント対応をしていると、いまだに「サニタイズ漏れ」という言葉を耳にする。だが、正直に言おう。人間が手動でサニタイズを行う時代はもう終わったんだ。

DOM型XSS(DOM-based XSS)は、サーバーサイドを通さず、クライアントサイドのJavaScriptだけで完結する攻撃だ。現代のSPA(Single Page Application)において、これは「コードのどこかに一つでも脆弱なシンク(危険な関数)があればアウト」という、極めてシビアな状況を作り出している。

今日は、そんな「モグラ叩き」のような脆弱性修正から君たちを解放する、Trusted Types APIという最強の切り札について解説する。

—

1. なぜ「サニタイズ」では不十分なのか

まず、敵を知ろう。DOM型XSSのPoC(概念実証)は驚くほど簡単だ。例えば、ユーザーの入力値をそのまま innerHTML に放り込んでいるアプリがあるとしよう。

// 攻撃者がURLパラメータに ?name= を仕込むと…
const params = new URLSearchParams(window.location.search);
const name = params.get(“name”);

// ここでDOMシンクが発火する
document.getElementById(“user-profile”).innerHTML = “ようこそ、” + name + “さん”;

開発者はここで「エスケープ関数を作ればいい」と考える。だが、複雑なフロントエンドコードの中で、すべての箇所に正しく適用するのは不可能に近い。「一度でもミスをすれば終わり」という設計自体が、セキュリティの敗北なんだ。

—

2. Trusted Types:ブラウザによる「強制的な型安全」

Trusted Typesは、DOMシンク(innerHTML, outerHTML, document.write 等)に対して、「ただの文字列」を渡すことを禁止する仕組みだ。

これを有効にすると、JavaScriptエンジンは「許可されたオブジェクト(Trusted Type)」以外が渡された瞬間にエラーを投げるようになる。つまり、開発段階で「型の不一致」としてバグを叩き潰せるわけだ。

導入ステップ:まずはポリシーを定義する

まずは、信頼できる変換ルール(Policy)を定義する。ここが唯一の「安全な場所」になる。

// セキュリティポリシーの作成
if (window.trustedTypes && window.trustedTypes.createPolicy) {
const policy = window.trustedTypes.createPolicy(‘myAppPolicy’, {
// 文字列を安全なHTMLオブジェクトに変換する関数
createHTML: (input) => {
// ここでDOMPurifyなどの信頼できるライブラリを通すのが鉄則
return DOMPurify.sanitize(input);
}
});

// 以後、innerHTML等にはこのpolicyを通したオブジェクトしか渡せない
const userContent = policy.createHTML(““);
document.getElementById(“profile”).innerHTML = userContent; // OK
// document.getElementById(“profile”).innerHTML = “…“; // ここでブラウザが例外を投げる!
}

—

3. Webアプリ全体を「強制」する(CSPの設定)

コードを書くだけでは甘い。もし誰かがポリシーを定義し忘れたら? そのために、HTTPレスポンスヘッダーで「Trusted Typesの使用を強制」する。

NginxやWebサーバーの設定ファイルに以下のCSP(Content Security Policy)を追加してほしい。

Nginxの設定例
add_header Content-Security-Policy “require-trusted-types-for ‘script’; trusted-types myAppPolicy;”;

  • require-trusted-types-for 'script':すべてのDOMシンクにTrusted Typesを強制する。
  • trusted-types myAppPolicy:許可するポリシー名を指定する。

これを設定した瞬間、君のアプリで「ポリシーを通していない文字列」を innerHTML に代入しようとすると、ブラウザは即座に実行をブロックし、コンソールに強力なエラーを表示する。これこそが、セキュリティの「防御的設計」だ。

—

4. 現場のエンジニアへ:明日からのアクションプラン

Trusted Typesを導入する際は、いきなり厳格に適用すると既存機能が全滅する。以下の手順で進めるのが、インシデントを発生させないプロのやり方だ。

1. レポートモードで試す:
Content-Security-Policy-Report-Only を使い、ブロックはせずにエラー箇所をログに流し続ける。これで「どこが脆弱か」の全貌が見える。
2. DOMPurifyの活用:
自作のサニタイズ関数など使うな。世界中の知見が詰まった DOMPurify をポリシー内に組み込むこと。
3. 段階的な適用:
まずは新規機能から導入し、古いモジュールはリファクタリングの過程でポリシーの対象にしていく。

最後に

セキュリティとは、「攻撃を防ぐこと」ではない。「脆弱性を生み出せない構造を作ること」だ。

Trusted Typesは、君たちのコードをより堅牢で、かつ「なぜ安全なのか」が説明できるものに変えてくれる。明日、チームのコードベースにこの仕組みを一行加えるだけで、将来の何千時間もの修正工数と、顧客からの信頼喪失という最悪の事態を防げるかもしれない。

さあ、退屈なサニタイズコードは捨てて、型安全な世界へ移行しよう。質問があれば、いつでも聞かせてくれ。

コメント

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