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

第8回 DOM-based XSS その3

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

あらかじめ定まった構造のHTMLをJavaScriptにて生成し,その一部にデータを当てはめる

たとえば「XMLHttpRequestなどを経由して外部からデータをJSONとして受け取り,それをもとに,特定書式のHTMLをJavaScript上で生成する」といった場合に,定められた形式に基づいたHTML断片をJavaScriptを使用して生成し,それをページの一部として埋め込むという処理が必要となることがあります。

具体例として,以下のコードを考えてみましょう。変数friendsには,外部に保存されているアドレス帳のデータが配列として保存されています。これを1つのエントリごとに変数templateで指定された書式に従って展開し,HTML上に表示するというコードです。多くの場合,こういった処理には何らかのJavaScript製のテンプレートエンジン(ライブラリ)やデータバインディング機構のあるフレームワークを使用することになると思いますが,ここではあくまでも説明のためにそういったライブラリやフレームワークを使用せずに,同種のコードを直接実装することにします。

// bad code
/* 
    変数friendsには以下のような配列が格納されている
    [
        {
            "name" : "山田太郎",
            "mail" : "yamada@example1.jp",
            "birthday" : "1980-05-19"
        },
        {
            "name" : "鈴木一郎",
            "mail" : "suzuki@example2.jp",
            "birthday" : "1991-10-22"
        },
        {
            "name" : "John Smith",
            "mail" : "john@example3.jp",
            "birthday" : "1993-3-27"
        }
    ]
*/

function expandTemplate (template, friends) {
    // テンプレート文字列中の「%name%」「%birthday%」「%mail%」をそれぞれ変数に置換する関数
    var i, s, html = "";
    for (i = 0; i < friends.length; i++) {
        s = template.replace(/%(\w+)%/g, function (s, param) {
            if (param === "name") return friends[ i ].name;
            else if (param === "birthday") return friends[ i ].birthday;
            else if (param === "mail") return friends[ i ].mail;
            else return "%" + param + "%";
        });
        html += s;
    }
    return html;
}

var template = '<div>%name% さん<br>メールアドレス:<a href="mailto:%mail%">%mail%</a> 誕生日:%birthday%</div>';
var elm = document.getElementById("contacts");
var html = expandTemplate(template, friends);
elm.innerHTML = html;

このようなコードでは,攻撃者がアドレス帳の中身をコントロール可能な場合にはXSSが発生することになります。たとえば,攻撃者が自身のコンタクト先情報として,以下のような名前とメールアドレスを設定していたとしましょう。

{
    "name" : "<img src=# onerror=alert(1)>",
    "mail" : "\"onmouseover=alert(2)//\"@example.jp",
    "birthday" : "2000-01-01"
}

攻撃者のこのようなコンタクト先が,ユーザーのブラウザ上でexpandTemplate関数によって処理された場合,生成されるHTMLは以下となり,名前欄とメールアドレス欄に攻撃者の仕込んだスクリプトalert(1)およびalert(2)がユーザのブラウザ上で動作してしまいます。

<div><img src=# onerror=alert(1)> さん<br>メールアドレス:<a href="mailto:"onmouseover=alert(2)//"@example.jp">"onmouseover=alert(2)//"@example.jp</a> 誕生日:2000-01-01</div>

このような処理でのDOM-basedの発生を防ぐ最もかんたんな方法は,テンプレートの文字列を変数と置換する際に,HTMLのメタキャラクタをエスケープすることです。

function htmlEscape (s) {
    s = s.replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#x27;");
    return s;
}

function expandTemplate (template, friends){
    var i, s, html = "";
    for (i = 0; i < friends.length; i++){
        s = template.replace( /%(\w+)%/g, function(s, param){
            if (param === "name") return htmlEscape(friends[ i ].name);
            else if (param === "birthday") return htmlEscape( friends[ i ].birthday);
            else if (param === "mail") return htmlEscape(friends[ i ].mail);
            else return "%" + param + "%";
        });
        html += s;
    }
    return html;
}

このように,従来サーバ上で行っていたエスケープと同様の処理をJavaScript上で行うことで,DOM-based XSSの発生を防ぐことができます。

先ほどの例と同様,攻撃者が自身のコンタクト先情報として以下のようなアドレスを設定していたとします。

{
    "name" : "<img src=# onerror=alert(1)>",
    "mail" : "\"onmouseover=alert(2)//\"@example.jp",
    "birthday" : "2000-01-01"
}

この場合であっても,テンプレート文字列に従ってコンタクト先情報を展開する際にJavaScriptでエスケープ処理を行っていれば,生成されるHTMLは以下のようになり,XSSは発生しません。

<div>&lt;img src=# onerror=alert(1)&gt; さん<br>メールアドレス:<a href="mailto:&quot;onmouseover=alert(2)//&quot;example.jp">&quot;onmouseover=alert(2)//&quot;example.jp</a> 誕生日:2000-01-01</div>

先にも述べたように,今回紹介したような定型的なHTMLを繰り返し生成するような処理では,一般的にはテンプレートエンジン(ライブラリ)やデータバインディング機構を持つフレームワークを用いることが多いと思います。ただし,そういったサードパーティ製のライブラリやフレームワークを導入する場合でも,それらによってHTMLを生成する際に,

  • 自動的にエスケープされた値が埋め込まれるのか
  • 明示的に指定した場合にのみエスケープされるのか

といった点は確認しておく必要があります。

また,使用するライブラリやフレームワークの新しいバージョンがリリースされた場合には,脆弱性の修正が含まれていないかを確認し,脆弱性の修正が含まれている場合にはライブラリを使用しているサイトにおいても新しいバージョンに更新するという作業を忘れてはいけません連載第6回参照⁠⁠。

著者プロフィール

はせがわようすけ

株式会社セキュアスカイ・テクノロジー常勤技術顧問。 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/