DOM Based XSS対策のChromeの新機能「Trusted Types」を試してみた
先日、Goole Chromeの新機能としてTrusted Typesという機能がアナウンスされました。これは、DOM Based XSSを防ぐために実装された機能です。
https://developers.google.com/web/updates/2019/02/trusted-types
本エントリでは、Trusted Types実際に試して、使い勝手や効果のほどを見てみようと思います。
DOM Based XSS
「DOM Based XSSとは」的な説明は割愛。DOMを更新するときに発生するXSSです。 上のリンクやIPA、OWASPのページなどを見てください。ここではIPAのレポートを紹介しておきます。
https://www.ipa.go.jp/files/000024729.pdf
DOM Based XSSの例
DOM Based XSSでalertを上げてみます。以下を参考に、脆弱性のあるHTMLファイルを用意します。
index.html
<html> <head> <title>DOM test</title> </head> <body> <script> hash=location.hash.slice(1); document.body.innerHTML=decodeURIComponent(hash); </script> </body> </html>
このHTMLでは、location.hash(URL内の#以降)を用いてDOMツリー(body要素のinnerHTML)を更新しています。以下のようなURLにアクセスすることでalertが上がります。
https://example.com/#%3Cimg%20src=x%20onerror=alert(1)%3E
hashに指定したimg要素がDOMツリーに追加されています。
DOM Based XSSの厄介なところ
脆弱性があるのか、チェックしづらいのが厄介です。上記はシンプルな例なのですぐわかりますが、実際にはjsライブラリ等も使うでしょうし、処理を追っていくのも一苦労。 動的に動かして確認するには腕とかに限界が来る。コードレビューとかで見つけようにも目とかに限界が来る。 個人的にはコードレビューした上で怪しいところを動的確認が現実的かなと思っています。
Trusted Types
DOM Based XSS対策のため、Google ChromeにTrusted Typesという機能が搭載されました。これを使うと、以下のコンセプトによりDOMの更新に制約がかかります。
- stringを使用してDOMを更新できなくなる
- 個別に定義したTrusted Type Policiesから生成されるTrusted Typesオブジェクトでのみ、DOMを更新できる
これにより、DOM Based XSSの入り込む余地をTrusted Type Policies内に限定できます。DOM Based XSS対策のためのコードレビューが楽になり、かつコードの品質が上がることが期待されます。
実際に例を見てみましょう。
準備
Trusted TypesはGoogle Chrome 73以降の機能なので、2019/02/20時点の安定版では使用できません。 いくつか準備が必要になります。
- 最新開発版をインストールする。僕はChrome Canary https://www.google.com/chrome/canary/ を使いました。
- Trusted Typesを有効にする。いくつか方法がありますが、僕は chrome://flags で設定しました。
chrome://flags/#enable-experimental-web-platform-features
- Webサーバ側で、Content-Security-Policyレスポンスヘッダを出力する。
Content-Security-Policy: trusted-types *
Trusted Typesの動作確認
これで、さきほどのHTMLにもう一度アクセスしてみます。
今度は実行されませんでした。Consoleログを見ると、innerHTMLの更新でTypeErrorとなっていることがわかります。
Uncaught TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires `TrustedHTML` assignment. at (index):8
Trusted Type Policiesを使って書き換え
Trusted Type Policiesを使って元のHTMLを書き換え、以前のようにDOMを更新できるようにしてみます。1
index2.html
<html> <head> <title>DOM test</title> </head> <body> <script> // define Trusted Type Policies const templatePolicy = TrustedTypes.createPolicy('template', { createHTML: (hash) => { if(hash) { return decodeURIComponent(hash.slice(1)); } throw new TypeError(); } }); // obtain Trusted Types object const htmlcode = templatePolicy.createHTML(location.hash); // htmlcode instanceof TrustedHTML document.body.innerHTML=htmlcode; </script> </body> </html>
簡単に言うと、templatePolicyポリシーを定義して、その中のcreateHTMLでDOMを更新するためのstringを作成しています。戻り値はTrustedHTMLオブジェクトになっていて、これでならDOMの更新が許可されているという形です。 2
変更したindex2.htmlにアクセスします。
https://example.com/index2.html#%3Cimg%20src=x%20onerror=alert(1)%3E
alertが上がりました。
DOM Based XSSをコードレビューで確認するにはTrusted Type Policiesを定義する部分だけをレビューすればいいので、レビュー工数の削減、コードの品質向上(チェック漏れを防ぐ)などが期待されます。
Trusted Typesは使えそうか
興味深いコンセプトですし、効果もある。個人的には期待しています。あるべき論でいうとDOMをHTMLで更新するなってところになると思うのですが、まあ動的にHTMLを変えたいニーズもあるでしょうし、そのときに効果的に対策できる機能があるのは良いことだと思います。 一方で、実用にはまだ課題もありそうです。僕の考える課題はこんなところです。
- 既存のプロダクトに取り入れるには、改修工数の面で難しそう
- モダンなjsフレームワークをバリバリ使っている場合でも、無理なく実装できるんだろうか 3
- 現状はChrome限定、かつ正式版でのリリース前なので、まだ対策として普及するには時間がかかりそう。すなわち人に推奨するのも難しい。 4
まとめ
DOM Based XSS対策としてTrusted Typesを紹介しました。Trusted Typesが、どのようにDOM更新を制限し、XSSが入り込む余地を制限しているかを、実際に動作させることにより確認しました。普及にはまだ時間がかかりそうですが、今後対応ブラウザが増えていけば有用な対策ツールになるのではないかと期待しています。