DOMベースXSS (DOM-based Cross-Site Scripting) の解説

DOMベースXSSは、Webアプリケーションのクライアントサイド(ブラウザ)で実行されるJavaScriptによって、悪意のあるスクリプトがページに挿入される脆弱性です。

サーバーサイドでの処理は関係なく、ブラウザがDOM (Document Object Model) を構築する際に、信頼できないデータが不適切に処理されることで発生します。

脆弱性のメカニズム

この演習では、URLのハッシュ部分(#以降)に商品IDが指定され、その商品IDをJavaScriptで取得してページ内に表示するショッピングサイトの商品ページを例にしています。

脆弱な商品ページ (dom-based-xss-vulnerable.php) では、以下のようにURLハッシュから取得した商品IDをinnerHTMLプロパティを使用して直接HTMLに挿入しています。


// URLのハッシュ部分から商品IDを取得
const hash = window.location.hash.substring(1);
let productId = '';
if (hash.startsWith('product_id=')) {
    productId = hash.substring('product_id='.length);
}

// 取得した商品IDをそのままHTMLに挿入(脆弱)
document.getElementById('product-id-display').innerHTML = decodeURIComponent(productId);
        

innerHTMLは、指定された文字列をHTMLとして解釈してDOMに挿入するため、商品IDとして#product_id=のような悪意のあるスクリプトが含まれていると、それが実行されてしまいます。

この例では、タグのsrc属性が不正な値であるため画像は表示されませんが、onerror属性に指定されたJavaScriptコード(alert(1))が実行され、XSSが成立します。

対策方法

DOMベースXSSの対策は、信頼できないデータをDOMに挿入する際に、そのデータをHTMLとして解釈させないようにすることです。

対策済み商品ページ (dom-based-xss-safe.php) では、以下のようにtextContentプロパティを使用しています。


// URLのハッシュ部分から商品IDを取得
const hash = window.location.hash.substring(1);
let productId = '';
if (hash.startsWith('product_id=')) {
    productId = hash.substring('product_id='.length);
}

// 取得した商品IDを安全にHTMLに挿入(対策済み)
document.getElementById('product-id-display').textContent = decodeURIComponent(productId);
        

textContentは、指定された文字列を純粋なテキストとしてDOMに挿入します。そのため、たとえ文字列にHTMLタグが含まれていても、それは単なる文字列として表示され、HTMLとして解釈されたり、スクリプトが実行されたりすることはありません。

ポイント

関連ページ