はじめに
— innerHTML があっても怖くない世界へ
XSS の中でも最も防ぎにくいのが DOM-Based XSS。
サーバー側のサニタイズが完璧でも、フロントエンドで innerHTML や insertAdjacentHTML を扱うだけで攻撃者に隙が生まれる。
そんな“フロント側の穴”を塞ぐために Google が提唱した最強の防御が Trusted Types(TT) だ。
Trusted Types の導入により:
- innerHTML 等の危険操作が すべてブロックされる
- 開発者は XSS-safe な専用 API 経由でしか HTML を扱えない
- CSP と組み合わせることで DOM XSS を ほぼ完全封殺 できる
この記事では、Trusted Types の仕組み・導入方法・活用パターンをわかりやすく解説する。
1. DOM-Based XSS の何が危険なのか?
DOM-Based XSS は、次のようなコードが原因で発生する。
element.innerHTML = location.hash; // 危険!
攻撃者が URL に次のような payload を入れるだけで:
https://example.com/page#<img src=x onerror=alert(1)>
ブラウザ側で 即座に悪意コードが実行される。
サーバーは関係ないため:
- WAF ではほとんど検出できない
- バックエンドのサニタイズも無力
- SPA フレームワークでも発生する
これが DOM XSS が“最後の難関”と言われる理由。
2. Trusted Types とは?(仕組みの核心)
Trusted Types の思想は非常にシンプル:
「innerHTML や document.write に“生の文字列”を入れることを禁止」
→ 安全と証明されたオブジェクト(TrustedHTML)しか受け付けない
これによって、攻撃者が悪意スクリプトを混入する余地がなくなる。
Trusted Types を有効にするには?
CSP に次を追加するだけ:
Content-Security-Policy: require-trusted-types-for 'script';
これでブラウザは:
- innerHTML
- outerHTML
- insertAdjacentHTML
- document.write
- setAttribute(on* 系)
などの危険操作に対し、
TrustedHTML 以外を完全拒否する。
3. Trusted Types の実装手順(最短 3 ステップ)
Step 1:CSP で Trusted Types を有効化
例:
Content-Security-Policy:
require-trusted-types-for 'script';
trusted-types default;
trusted-types default;
は「default」というポリシー名で実装を受け付ける意味。
Step 2:Trusted Types ポリシーを定義
フロント側 JS に次を追加:
window.trustedTypes.createPolicy("default", {
createHTML(input) {
return input; // ここにサニタイズ処理を入れる
}
});
createHTML() が「安全な HTML を作る唯一の入口」。
Step 3:危険 API を TrustedHTML 経由で使用する
const safe = trustedTypes.getPolicy("default").createHTML(
sanitize(userInput)
);
element.innerHTML = safe; // OK(TrustedHTML)
生の文字列を innerHTML に入れるとどうなる?
element.innerHTML = userInput;
// → CSP エラー(XSS 防止成功)
Trusted Types が完全に XSS を遮断してくれる。
4. Trusted Types の効果(攻撃者視点で見ると最強)
攻撃者が典型的な payload を試しても…
javascript:alert(1)
<img src=x onerror=alert(1)>
<svg><script>alert(1)</script>
これらが innerHTML に到達した瞬間、ブラウザが:
「TrustedHTML じゃないので拒否します」
とブロックする。
DOM XSS の最大の弱点だった
“開発者のミス” を、Trusted Types がルール化して潰してくれる。
5. 実務で使えるサニタイズパターン
Trusted Types と組み合わせる最強の方法は:
DOMPurify + Trusted Types + CSP
const policy = trustedTypes.createPolicy("default", {
createHTML(input) {
return DOMPurify.sanitize(input, {RETURN_TRUSTED_TYPE: true});
}
});
こうすれば:
- DOMPurify が悪意 HTML を削除
- Trusted Types が“危険 API の入口”を封鎖
- CSP が JS 実行をさらに制限
三段構えで XSS を防げる。
6. Trusted Types の注意点(落とし穴)
| 落とし穴 | 説明 |
|---|---|
| SPA フレームワークで影響出る可能性 | React/Vue/Angular が内部で innerHTML を使っている場合あり |
| 初期導入時エラーが大量発生する | TT がブロックしている証拠だが、修正コストが発生 |
| 柔軟すぎる createPolicy 実装 | ポリシーがザルだと意味がない |
特に React の dangerouslySetInnerHTML は、
Trusted Types と併用する場合は専用設定が必要。
まとめ — Trusted Types は DOM XSS の“最終兵器”
Trusted Types を導入すると:
-
innerHTML を直接使えなくなる
→ 開発者のミスで DOM XSS が発生しない
-
TrustedHTML だけ許可される
→ 悪意 HTML が通らない
-
CSP と組み合わせると「ほぼ完全防御」
→ XSS の攻撃面が激減
-
DOMPurify と併用すると実務最強構成
→ Web アプリ全体の堅牢性が大幅アップ
Trusted Types は「現代の Web が抱える DOM XSS 問題に対する決定的な解決策」。
Google、Gmail、YouTube、Twitter など大規模サービスで採用されている理由はそこにある。