今回はTrusted Typesに対する個人の見解を書いてみます。
Trusted Typesはブラウザが文字列を文字列以外の型として扱うSinkに対して、開発者に型の変換を強制するセキュリティ機能です。Trusted TypesによりDOM-based XSSを原理的に減らし、DOM-based XSSに対するセキュリティレビューを簡潔にすることが出来ます。
安全でないデフォルト
近頃のWeb開発ではTypeScriptがよく使われるようになりました。これは型を明示することにより、エラーを事前に防げるからです。
セキュリティでも同じことが言えます。そもそもelement.innerHTMLにStringを代入出来ること自体が間違っているのです。innerHTMLはHTMLを代入する為のものであり、Stringを代入してもHTMLとして型の変換がされてしまうからです(i.e. re-parsing)。
同様に、scriptElement.textにStringを代入すべきではなく、Scriptを代入するべきなのです。
このように、Web開発ではHTMLやScriptなどをStringとして扱える安全でないコーディングがデフォルトでした。Trusted TypesはTrustedHTML、TrustedScript、TrustedScriptURLという3つの型を提供し、Opt-inでこの型に変換されるSinkに対して型のランタイム確認をすることができます。
なぜ現状のCSP script-srcではダメなのか
CSPのscript-srcは開発者が意図して読み込んだスクリプトを明示することにより、信頼するスクリプトのみを実行することが出来るセキュリティ機能です。しかし、Script Gadgetsなどの研究により、よく使われるフレームワークやライブラリのコード内にDOM-based XSSを引き起こすコードがあることが分かってきました。その為、CSPのscript-srcにより緩和されていたと思われていたXSSが場合によっては緩和出来ていないことが分かってきました。
なぜTrusted Typesなのか
Trusted TypesはWebアプリ内で実行されている全てのコードに対して型の確認がされます。その為、Webアプリで許可されているポリシーの安全性が確保出来ればDOM-based XSSがないということがほどんど担保できます。逆に言えば、DOM-based XSSに対するセキュリティレビューはTrusted Typeポリシー内のコードだけを読めばよくなります。これはSPAなどのStored XSSやReflected XSSがないウェブアプリからはXSS自体がなくなることを意味します。
更に最近ではPrototype Pollutionを使ってXSSの脆弱性がないアプリでも、プログラムのフローを変えて最終的にXSSに持っていくという手法があります。このような場合、Trusted Typesがデプロイされている環境であれば、危険なSinkを使うコードが極端に減るため、原理的にXSSに辿り着くことが難しくなります。
また、Prototype Pollutionを使ってHTML Sanitizerのコンフィグを改ざんするような手法でも、今後出てくるSanitizer APIを使うことによってXSSが発生するHTMLは返らないようになります。
(ただPrototype PollutionはXSSが出来なくても他の方法で悪用が出来ると思います)
Strict CSPとTrusted TypesをやってればXSSは完全に緩和できる?
いくつか分かっている問題があります。
Dynamic importにはTrustedScriptURL型が強制されるべきだが、多分Dynamic importがTC39の持ち物なので、それが叶わないと思われる。一応この問題を解決する為にDynamic Import Host Adjustmentという提案がされている。この問題はscript-srcにスクリプトを読み込めるオリジンのリストを指定することにより緩和できる。
Strict CSPでは'strict-dynamic'が許可されている為、Templateタグ内のHTMLをDOMに移すようなガジェットがある場合、Stored XSSかReflected XSSがあればXSS出来る可能性がある。この問題は'strict-dynamic'を無くしたNonceのみのCSPにより緩和できる。
終わりに
最近Trusted Typesに注目していたので、そこらへんの理由をまとめてみました。
次回、時間があれば既存のコードをどうやってTrusted Typeしていくか、みたいな記事を書こうかと思ってます。