公開日:2007年6月28日
独立行政法人情報処理推進機構
セキュリティセンター
本ページの情報は2007年6月時点のものです。
記載の資料は資料公開当時のもので、現在は公開されていないものも含みます。
近年、Webアプリケーションの構成が変化しつつある。多くのJavaScriptコードがWebブラウザで動作するようになり、その背後ではサーバと連携してリッチかつスムーズな操作感を提供するような構成が普及している。いわゆる Ajaxタイプのコンテンツが増えている。
このような環境において、WebクライアントのJavaScriptプログラムを標的とするスクリプト注入攻撃がありうる。いわゆる「DOMベースのスクリプト注入」である。この攻撃についての対策を解説する。
いわゆる「DOMベースのスクリプト注入」攻撃は、従来のWebサーバ側プログラムのエコーバック(鸚鵡返し)ロジックを悪用するスクリプト注入攻撃(注釈1)とは事情が異なる。ブラウザ内で実行され、サイト横断的な構図にはならない。DOM等のブラウザオブジェクトの細部についての理解が不可欠である。
Ajaxタイプの動的コンテンツは、ユーザによる画面操作の解釈と応答の多くをコンテンツ内のJavaScriptプログラムで行う。ユーザへの応答の画面表示を行うために、JavaScriptプログラム内部では、DOMを含むブラウザオブジェクトツリーの一部分を動的に書き換えることが行われる。
DOMオブジェクトツリーを構成する document オブジェクトは、window 配下にある。window オブジェクトが、ブラウザによって表示されているウィンドウに対応するグローバルオブジェクトなのである。window の location プロパティが location オブジェクトであり、現在表示されているURI に関する情報が格納される。location オブジェクトが更新されれば document オブジェクト全体が更新される関係にある。
JavaScriptプログラムは、DOM、ブラウザオブジェクト、ECMAScriptオブジェクトを操作する。この際に API を直接操作することもできるが、jQuery のライブラリを用いて操作するのが効率的であろう。
いわゆる「DOMベースのスクリプト注入」を行う代表的な手口は、書き換えの際にオブジェクトツリーに悪意のスクリプト要素を接続させて、それを実行させるようにするものである。
例えば、コンテンツが呼び出された際のURI上のクエリ文字列(?xxxの部分)を自ら取り込み Query: [ … ] と表示することを意図した次のようなHTMLコンテンツがあるとする。
[例 HTMLコンテンツ]
…
Query: [<span id="span1"></span>]
…
<script>
var span1 = document.getElementById ("span1");
var query = decodeURI (location.search).substr (1);
span1.innerHTML = query;←※1
</script>
これを次のようなURIで呼び出す。
http://ホスト/パス?<img src=a onerror="alert(3)"></img>
実際には適切なエンコードを施すものとする。すると、innerHTMLプロパティへの代入によって<span>要素へ<img>要素が接続されるがその際に、<img>タグに仕込まれている onerrorハンドラのスクリプトが実行されてしまう。 すなわち、 location.search から悪意のスクリプトが入り、DOM 要素の innerHTML プロパティに注入されてしまうのである。
サーバ上のプログラムが、信頼できない入力データをそのままブラウザへ向けて送り出すと脆弱性が生じるのと同様に、クライアントで動作する JavaScriptプログラムにおいては、信頼できない入力データをそのままオブジェクトツリーに書き込むことによって、スクリプト注入されうる脆弱性が生じる。
信頼できないデータが入ってくる経路には複数の種類がありうる。特に、Webページの構成要素のいくつかを、他者運営の必ずしも高い信頼をおけないサーバから取得する場面では、それらに攻撃スクリプトが潜んでいることを警戒する必要がある。
また、侵入してきた攻撃パターンがスクリプトとして作動してしまう場面にも複数の種類がある。
jQuery は、JavaScript 用ライブラリである。jQuery を使うと、DOM に対する読み出しと変更を手短かに記述できる。
例えば、jQuery を使った次のコードは、Webページの中から class="alfa" の属性をもつタグをすべて見つけ出し、それらのテキストをすべて hello に設定した上で文字の色を赤くするというものである。
$(".alfa").text ("hello").css ("color", "red")
jQuery を使用するプログラミングにおいて、スクリプト注入を特に警戒すべきなのは次の3箇所である。
これらの箇所ではHTML文字列の解釈が行われる。ここに信頼できない入力値(location.hash 等)をそのまま渡すと、スクリプト注入を招くおそれがある。
$() は複数の機能を兼ね備えた関数である。中でも主要な機能は、DOMの中を検索して条件に合った要素の集合を得ることと、これからDOMへ追加すべき新たな要素を作り出すことである。(下位要素を従えることができる。)
DOM要素を検索するつもりであっても、引数に次のような文字列が含まれていると、DOM要素を作り出す機能の方が働いて onerror= に仕込まれているスク リプトが動作してしまう。
<img src="/" onerror="...">
対策は、$() の引数にはリテラルのみ与えるようにすることである。変数を含む式を用いて検索条件を与えたい場合は、検索専用の find() を用い、次のように記述する。
$(document).find ( 式 ) もしくは
$("#id").find ( 式 )
html() は、$() や $(...).find() によって得られたDOM要素(ここでは x としている)へ下位の要素(タグ)を追加する関数である。引数にHTML文字列を与えるものであるため、スクリプト注入を招くおそれがある。
対策は、タグはリテラルで記述し、可変要素を text() を用いて設定するようにすることである。例えば、
x.html("<ul><li>" + 項目1 + "</li><li>" + 項目2 + "</li></ul>");
とする代わりに、次のように記述する。
x.empty ().append (
$("<ul></ul>")
.append ($("<li></li>").text ( 項目1 ))
.append ($("<li></li>").text ( 項目2 ))
);
$.parseHTML() は、HTML文字列を処理しその解析結果を返す関数である。次のように用いて新たなDOM要素(下位要素を含む)を生成するのに用いられる。
$($.parsetHTML( HTMLソース文字列 ))
$("<タグ>...") と類似しているが、引数に与える文字列が必ずしも「<」で始まらなくてもよい点が異なる。
この関数も html() と同様、引数にはHTML文字列を与えることになるため、スクリプト注入を招くおそれがある。対策も、html() の対策に準ずる。
クライアント側コードに起因するスクリプト注入に対しては次のように対策する。
警戒すべきソース(入力地点)には、下記の要素等がある。
コンテンツのURIを参照/変更できるオブジェクト。値を参照すると、location.hrefと同じ値が得られる。
コンテンツのURIのフラグメント部分
コンテンツのURIのクエリ部分
コンテンツのURIのパス部分
コンテンツのURIの全体
コンテンツのURI
コンテンツのURI。document.URL と同じ値をもつ。DOM 4で提案されている。
location (document. で修飾されない)と同じオブジェクトを指す
このコンテンツを起点として相対URIを解決する際に用いられる基準のURI。ドキュメント中に<base>タグ等による明示がないときは、document.URLと同じ値をもつ。
このコンテンツへのリンクが書かれていたWebページのURI
コンテンツに関連づけられているクッキーすべてが並べられた文字列
ウィンドウの名前
Web Messaging受信ハンドラ関数の引数のdataプロパティ
警戒すべきシンク(出力地点)には下記のものがある。DOMオブジェクトツリーを構成するdocumentオブジェクト配下以外にも警戒すべきシンクがある。
ECMA仕様に規定されている eval系関数:
ECMA仕様にも W3C仕様にも規定されていないが大部分のブラウザが備えているもの:
Internet Explorer固有のもの: