従来サーバー上だけで生成していたHTMLを、Ajaxなどと組み合わせブラウザー上でJavaScriptによって操作する機会が増えたため、JavaScriptによるDOM操作時に発生するXSS、すなわちDOM Based XSSが急増しています。
IPAが「DOM Based XSS」の脆弱性に関するレポートを公開
http://www.atmarkit.co.jp/ait/articles/1301/29/news155.html
DOM Based XSSにおいて、攻撃者によってスクリプトが含まれる可能性のある箇所を「ソース」、JavaScriptプログラム内でそのソースを用いることによって実際にXSSが発生する原因となる機能のことを「シンク」と一部の研究者は呼んでいます。
実際にソースとして働く機能の例としては以下のようなものがあります。
location.href
location.search
location.hash
document.cookie
document.referrer
window.name
localStorage
sessionStorage
シンクとして働く機能の例としては以下のようなものがあります。
document.write
element.innerHTML
eval
setTimeout
setInterval
jQuery(),$(), $.html()
もちろんこれらは代表的な例であり、ソース、シンクともこれら以外にもさまざまなものがあります。必ずしもこの「ソース」「シンク」という用語を覚えておく必要はありませんが、DOM Based XSSに関する理解を深めるためには役に立ちますので紹介しました。
DOM Based XSSへの具体的な対策としては、(1) 使用しているJavaScriptライブラリの適切なバージョンアップに加え、(2)DOM操作APIの適切な使用、(3)扱うURLをhttpおよびhttpsに限定する、の3点になります。
現在多くのWebサイトではjQueryをはじめ、多数のJavaScriptライブラリが使用されていますが、これらのライブラリについても脆弱性が発見、報告されることがあります。
サイトを構築後も、JavaScriptライブラリにセキュリティ修正を行ったバージョンがリリースされた場合には、サイトで利用しているライブラリを更新する必要があります。
DOM Based XSSは、JavaScript上でHTMLを組み立てる場合に、攻撃者の用意した文字列がHTML内にそのまま挿入されることによって発生するXSSです。
DOM Based XSSにおいても、XSS対策の原則である「HTML生成時におけるエスケープ」に従い、シンクへ文字列を与える時点でエスケープ処理を行うという方法は有効ですが、可能であれば文字列を操作するのではなく、DOMを通じてHTMLを生成、操作する方が、原理的にXSSの発生を抑えやすくできます。
例えば、DOM Based XSSを発生させる典型的なコードの例として、以下のようなinnerHTMLの使用があったとします。
// ★★★脆弱なコードの例★★★ var div = document.getElementById( "msg" ); div.innerHTML = some_text; // 外部からコントロール可能な文字列
これに対して、従来サーバー側で行われてきた文字列をエスケープするという方法をそのままブラウザー上で行うと以下のようになります。
// 安全なコードの例。★★★決して最適な方法ではない(後述)★★★ function escape_html( s ){ return s.replace( /&/g, "&" ) .replace( /</g, "<" ) .replace( />/g, ">" ) .replace( /"/g, """ ) .replace( /'/g, "'" ); } var div = document.getElementById( "msg" ); // some_text は外部からコントロール可能な文字列 div.innerHTML = escape_html( some_text );
この方法でも安全にはなりますが、自由にDOMを操作できるブラウザー上のJavaScriptであれば、DOM APIを経由して操作する方が、原理的に確実にXSSの発生を抑えやすく、またより自然で見通しのよいコードにもなります。
// ★★★安全なコードの例(推奨)★★★ var div = document.getElementById( "msg" ); // some_text は外部からコントロール可能な文字列 var text = document.createTextNode( some_text ); div.appendChild( text );
jQueryであれば、$( selector ).html( some_text ); ではなく、$( selector ).text( some_text )を使うことになります。
テキストノードだけでなく、属性についても同様にDOM APIを経由して操作することが望ましいといえます。
// ★★★脆弱な例★★★ var div = document.getElementById( "msg" ); var url = "http://example.jp/" + some_page; // some_page は外部からコントロール可能な文字列 div.innerHTML = '<a href="' + url + '">' + url + "</a>";
// ★★★安全な例★★★ var div = document.getElementById( "msg" ); var url = "http://example.jp/" + some_page; // some_page は外部からコントロール可能な文字列 var elm = document.createElement( "a" ); elm.setAttribute( "href", url ); elm.appendChild( document.createTextNode( url ) ); div.appendChild( elm );
Copyright © ITmedia, Inc. All Rights Reserved.