JavaScriptセキュリティの基礎知識

第6回 DOM-based XSS その1

この記事を読むのに必要な時間:およそ 6 分

JavaScriptによるブラウザ上での処理量およびコード量の増加に伴い,JavaScript上のバグが原因で発生する脆弱性も増加しています。そのような脆弱性の最も代表的なものが,DOM-based XSSです。今回から数回に分けて,DOM-based XSSについて説明していきます。

DOM-based XSSとは

本連載第2回で説明したような一般的な反射型および蓄積型のXSSのほとんどは,Webアプリケーションがサーバ上でHTMLを生成する際に,攻撃者が指定した文字列のエスケープが漏れていることが原因で発生します。一方,DOM-based XSSは,サーバ上でのHTMLの生成時には問題はなく,ブラウザ上で動作するJavaScript上のコードに問題があるために発生します。

たとえば,以下のようなJavaScriptコードがあったとします。

// bad code
div = document.getElementById("info");
div.innerHTML = location.hash.substring(1); 

このコードは,URL中の#より後ろの部分の文字列をHTML内に表示するだけのものですが,攻撃者によって誘導されたユーザーが「http://example.jp/#<img src=1 onerror=alert(1)>」のようなURLをIEやEdgeで開いた場合には,#以下の部分がHTML内に展開され,攻撃者の用意したJavaScriptがユーザーのブラウザ上で動いてしまいます※1)。

このように,ブラウザ上で動くJavaScriptによって発生する種類のXSSをDOM-based XSSと呼びます。

これまではサーバ上でHTMLの生成が行われていましたが,ブラウザ上でJavaScriptによってHTMLを操作・生成する機会が増えているため,DOM-based XSSの発生する割合もそれに合わせて近年増加しています。

また,動的にデータを処理する,いわゆるWebアプリケーションだけでなく,静的なHTMLのみを配信している場合であっても,メニューなどHTMLの一部分をアニメーション動作させたり,スマートフォンとPCなどデバイスごとに表示を最適化するなどのためにJavaScriptを使用している場合,それらのJavaScriptコードが原因でDOM-based XSSが発生することもあります。

※1
Mozilla Firefoxでは,URL中の#以降が%にエンコードされるため,タグ扱いされません。また,Google Chromeでは,XSS Auditorによりスクリプトはブロックされる可能性があります。

DOM-based XSSが厄介な理由

IEやEdgeのXSSフィルタをはじめ,現在の多くのブラウザでは,単純なXSSからユーザーを保護するための機能が備えられています。ブラウザによって差はありますが,「リクエスト内に含まれる文字列がレスポンス内でスクリプトなどとして現れた場合には,反射型のXSSであるとみなして,その動作をブロックする」というのが基本的な原理です。

しかし,JavaScriptによってクライアント上でHTMLを組み立てると,攻撃用のコード部分がサーバにリクエストとして送信されないため,ブラウザの備えているXSS保護機構を回避して攻撃が成立しやすい傾向があります。また,location.hash内に攻撃コードを含めている場合には,攻撃コードがサーバ側に送信されないことになり,万が一XSSによって多数の被害が発生したとしてもサーバ側にはそのログが残らず,被害の把握が困難になることも考えられます。

さらに,従来からの検査手法では脆弱性の存在を発見することが非常に困難なことも問題です。通常のWebアプリケーションの脆弱性診断では,検査ツールから対象のWebサーバに対してリクエストを発行し,そのレスポンスの内容によってサーバ側に脆弱性が存在するかどうかを調べるといった手法が一般的ですが,DOM-based XSSではクライアント上でJavaScriptが動作するまではXSSが発生しません。

それにも関わらず,攻撃者は対象となるWebアプリケーションが実際に使用しているJavaScriptコードを丹念に読むことでDOM-based XSSを探し出すこともできるという,現在のところまさに攻撃者にとって有利としか言いようのない状況がそろっていると言っても過言ではありません。

DOM-based XSSの原因 ~シンクとソース

かんたんなDOM-based XSSの例として,先に挙げたJavaScriptコードをもう一度見てみましょう。攻撃者は,ユーザーを「http://example.jp/#<img src=1 onerror=alert(1)>」に誘導することによって,location.hash内に実行させたいコードを含めることができます。そして,それがユーザーのブラウザ上でDOM要素のinnerHTMLへと代入されることで,XSSが成立します。

このとき,攻撃者が実際の攻撃のためのJavaScriptコードを含めていたlocation.hashのような箇所を「ソース」といいます。また,ソースに含まれる文字列を受け取り,文字列からJavaScriptを生成,実行してしまう箇所のことを「シンク」といいます。ソース,シンクという用語は広く普及しているわけでもなく,必ずしも知らなければいけないというものではありませんが,DOM-based XSSに関する理解を深めるためには便利な概念なので,覚えておくといいかと思います。

DOM-based XSSのソースとして働く機能の代表例としては,以下のようなものがあります。

  • location.hash
  • location.search
  • location.href
  • document.cookie
  • document.referrer
  • window.name
  • Web Storage
  • IndexedDB
  • XMLHttpRequest.responseText

一方,DOM-based XSSのシンクとして働く機能の代表例としては,以下のようなものがあります。

  • HTMLElement.innerHTML
  • location.href
  • document.write
  • eval
  • setTimeout, setInterval
  • Function
  • jQuery(), $(), $.html()

これら以外にも,ソースあるいはシンクとして働く機能にはさまざまなものがあります。

攻撃者がソースに与えたJavaScriptが,さまざまな処理を経て,最終的にシンクとなる機能に渡ることにより,DOM-based XSSが発生することになります。

DOM-based XSSが発生するまでの流れ

DOM-based XSSが発生するまでの流れ

シンクにはさまざまなものがありますが,DOM-based XSSを発生させる主要な原因としては,innerHTMLやdocument.writeといったシンクが文字列からHTMLを生成する場合がほとんどを占めています。つまり,「ソースに与えられたデータを処理し,シンクに至る」という経路上のどこかの点で,攻撃者が与えた文字列から「<」「>」あるいは「"」などを検査,排除できれば,DOM-based XSSの多くを防ぐことができるということになります。

ただ,データの入力時に危険と思われる文字列を排除あるいは加工する方法は,プログラムの規模が大きくなると破綻しやすくなります。従来サーバ上で行っていたXSS対策の原則である「HTMLを生成する時点でエスケープして出力する」という考え方をブラウザ上の処理にも適用すれば,「ブラウザ上での最終的なレンダリング時に,JavaScriptによって文字列をエスケープして出力する」というのが合理的といえるでしょう。もちろん,JavaScriptであれば,HTMLの操作のために文字列ではなくDOM経由で値の設定ができるので,適切なDOM操作関数を選ぶことで文字列のエスケープという操作は不要になります。

また,JavaScriptによるHTMLの操作では,「<」「>」の挿入によるXSSだけでなく,javascript:スキームやvbscript:スキームへのリンクを作成することによって発生するXSSにも注意する必要があります。

さらに,JavaScriptライブラリに含まれる脆弱性が原因でXSSが発生することもあるので,「使用しているJavaScriptライブラリの新しいバージョンがリリースされたときには脆弱性の修正が含まれていないかを確認し,必要に応じて更新する」という,これまでサーバ側で使用しているライブラリやミドルウェアなどで行っていたことと同じ作業をJavaScriptライブラリについても行う必要があります。

著者プロフィール

はせがわようすけ

株式会社セキュアスカイ・テクノロジー常勤技術顧問。 Internet Explorer,Mozilla FirefoxをはじめWebアプリケーションに関する多数の脆弱性を発見。 Black Hat Japan 2008,韓国POC 2008,2010,OWASP AppSec APAC 2014他講演多数。 OWASP Kansai Chapter Leader / OWASP Japan Board member。

URL:http://utf-8.jp/

コメント

コメントの記入