Trusted Typesのポリシーを作っただけで、DOMへのすべての文字列入力が拒否されるわけではありません。対応ブラウザーでCSP require-trusted-types-for 'script'をenforcementとして設定すると、指定sinkで通常文字列を拒否しTrusted Types値を要求できます。Trusted Types仕様とCSP Level 3が仕組みを定義し、web.devの専門ガイドが導入を説明します。

入力がポリシーとサニタイズを経て保護対象DOM sinkへ届く概念図

概念図であり、セキュリティテストや画面キャプチャではありません。

sinkに合う型を使う

TrustedHTMLはHTML sink、TrustedScriptは指定script sink、TrustedScriptURLはscript URL sink向けです。CSPの'script'はすべてのDOMメソッドを拒否する意味ではありません。対象sinkの範囲を仕様と対象ブラウザーで確認します。HTML解釈が不要ならtextContentなどを優先します。

ポリシー名は安全の証明ではない

trustedTypes.createPolicyにはアプリ側の変換関数を渡しますが、ブラウザーがその処理の安全性を証明するわけではありません。入力をそのまま返すポリシーをTrustedHTMLで包んでもXSS対策にはなりません。レビュー済みsanitizerを使い、用途に応じて要素、属性、URL schemeを制限します。

enforcementとreport-onlyを区別

require-trusted-types-forは対象sinkで型を強制します。trusted-typesは作成可能なポリシー名を絞ります。report-onlyは違反の観察に使い、値を実際に拒否するものではありません。報告から既存sinkを把握し、ポリシーと対応ブラウザーを検証してからenforcementへ移行します。

他の防御も続ける

Trusted TypesはDOM XSSの攻撃面を狭める一層であり、入力検証、文脈に応じた出力処理、安全なDOM API、他のCSP設定を置き換えません。未対応ブラウザーで同じ強制を期待しないでください。ポリシー生成箇所と重要sinkをレビューし、許可名を必要以上に広げないようにします。

既存コードへ段階的に導入

まずreport-onlyなどの非強制段階で通常文字列を受け取るsinkを把握し、呼び出し経路ごとにユーザー入力、サーバーデータ、信頼済みtemplateを分けます。型を作る境界でポリシーが実際のsanitizerを呼ぶか確認し、CSP allowlistでポリシー名を絞ります。default policyは変換漏れの観察や一時的な互換に使えますが、広く素通しすると強制の目的が弱まります。

enforcement後は対応ブラウザーと旧ブラウザーを確認し、拒否されたsinkが機能を壊さないか試します。許可するmarkup、URL scheme、失敗時の動作をレビューしてください。TrustedHTMLが返っただけで全入力を安全と扱わず、拒否入力を緩いHTMLとして再試行する実装は新たな迂回経路になり得ます。

参考資料