2021年6月12日土曜日

Trusted Typesの概念と背景

今回はTrusted Typesに対する個人の見解を書いてみます。


Trusted Typesはブラウザが文字列を文字列以外の型として扱うSinkに対して、開発者に型の変換を強制するセキュリティ機能です。Trusted TypesによりDOM-based XSSを原理的に減らし、DOM-based XSSに対するセキュリティレビューを簡潔にすることが出来ます。

安全でないデフォルト

近頃のWeb開発ではTypeScriptがよく使われるようになりました。これは型を明示することにより、エラーを事前に防げるからです。

セキュリティでも同じことが言えます。そもそもelement.innerHTMLStringを代入出来ること自体が間違っているのです。innerHTMLはHTMLを代入する為のものであり、Stringを代入してもHTMLとして型の変換がされてしまうからです(i.e. re-parsing)。

同様に、scriptElement.textStringを代入すべきではなく、Scriptを代入するべきなのです。

このように、Web開発ではHTMLScriptなどをStringとして扱える安全でないコーディングがデフォルトでした。Trusted TypesはTrustedHTMLTrustedScriptTrustedScriptURLという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していくか、みたいな記事を書こうかと思ってます。

2021年3月31日水曜日

A Hack to render untrusted content in an isolated process

Sometimes, Web apps needs to render untrusted content in their websites, where the content can execute script. And most of them are implemented in one of the following 2 ways.

  1. Create a sandboxed domain, where user can execute script but has no harm in the main product (e.g. *.googleusercontent.com)
  2. Render the content using sandboxed iframe, with Data URL or srcdoc (i.e. the script will have opaque origin).
With Spectre, the latter option becomes vulnerable 😕

Site Isolation

At the time of writing, Site Isolation is fully enabled only on desktop platforms of Chromium . So this post will only focus on the desktop platforms.

While Site Isolation takes care of isolating cross-site documents (i.e. option #1), it doesn't take care of same-site sandboxed iframes at the time of writing. Data URL and srcdoc (i.e. about:srcdoc) are considered same-site to the navigation initiator.

Therefore, script executing inside Data URL or srcdoc sandboxed iframes can read data of parent frame (i.e. the navigation initiator).

A real world example

Google Earth has a feature to import KML map. Google Earth renders KML map in a sandboxed iframe with srcdoc. And because KML map can have arbitrary contents including scripts, this allows an attacker with Spectre exploit to read data of earth.google.com.

This bug was reported to Google but marked as Won't Fix 😂

The following PoC shows the ability for an attacker to execute script inside the sandboxed iframe by showing a random YouTube video 😁

(* Audio is loud so turn off if you don't want to listen 😊)

A Hack to render untrusted content in an isolated process 

As you might have guessed from the title of this blog post, there is a solution to this problem 😉
I've mentioned in my Site Isolation blog post, that a Blob URL with opaque origin will have its process isolated based on origin AND path.
We can use this fact to create a Blob URL from an opaque origin document. Which will result in creating a Blob URL with opaque origin, which is guaranteed to have an isolated process per new Blob URL if navigated.

The following PoCs shows this in action 😎

Conclusion

If your site renders untrusted scripts using sandboxed iframe with either srcdoc or a Data URL, you should use a Blob URL with opaque origin instead 🙂