新標籤的XSS
HTML5 定義了很多新的標籤、事件,這有可能帶來新的XSS攻擊。
一些XSS Filter 如果建立了一個黑名單的話,則可能就不會覆蓋到HTML5新增的標籤和功能,從而避免發生XSS。
筆者曾經在百度空間做過一次測試,使用的是HTML5新增的<video>標籤,這個標籤可以在網頁中遠程加載一段視頻。與<video>標籤類似的還有<audio>標籤,用於遠程加載一段音頻。
<video src="http://tinyvid.tiv/file/29d6d90ad.ogg"
onloadedmetadata="alert(document.cookie);" ondurationchanged="alert(//XSS2/);"
ontimeupdate="alert(/XSS1/);" tabindex="0"></video>
成功地繞過了百度空間的XSS Filter:
HTML 5 中新增的一些標籤和屬性,使得XSS等Web攻擊產生了新的變化,爲了總結這些變化,有安全研究者建立了一個HTML 5 Security Cheatsheet 項目,此項目對研究HTML 5 安全有着重要作用。
iframe 的 sandbox
<iframe> 標籤一直以來都爲人所詬病。掛馬、XSS、ClickJacking等攻擊中都能看到它不光彩的身影。瀏覽器廠商也一直在想辦法限制iframe執行腳本的權限,比如跨窗口訪問會有限制,以及IE中的 <iframe> 標籤支持 security 屬性限制腳本的執行,都在向着這一目標努力。
在HTML5中,專門爲iframe 定義了一個新的屬性,叫 sandbox。使用 sandbox 這個屬性後,<iframe> 標籤加載的內容將爲視爲一個獨立的“源”,其中的腳本將被禁止執行,表單被禁止提交,插件被禁止加載,指向其他瀏覽對象的連接也會被禁止。sandbox 屬性可以通過參數來支持更精確的控制。有以下幾個值可以選擇:
- allow-same-origin:允許同源訪問;
- allow-top-navigation:允許訪問頂層窗口;
- allow-form:允許提交表單;
- allow-script:允許執行腳本。
可有的行爲即便是設置了allow-scripts,也是不允許的,比如“彈出窗口”。
一個iframe的實例如下:
<iframe sandbox="allow-same-origin allow-forms allow-scripts"
src="http://maps.example.com/embedded.html"></iframe>
毫無疑問,iframe的sandbox屬性將極大地增強應用使用iframe的安全性。
Link Types:noreferrer
在HTML 5 中爲<a>標籤和<area>標籤定義了一個新的Link Types:noreferrer。
標籤指定了 noreferrer 後,瀏覽器在請求該標籤指定的地址時將不再發送Referrer。這個標籤需要開發者手動添加到頁面的標籤中,對於有需要的標籤可以使用noreferrer。
<a href="xxx" rel="noreferrer" >test</a>
這種設計是出於保護敏感信息和隱私的考慮。因爲通過Referrer,可能會泄露一些敏感信息。
Cross-Origin Resource Sharing
瀏覽器實現的同源策略(Same Origin Policy)限制了腳本的跨域請求。但是互聯網的發展趨勢是越來越開放的,因此跨域訪問的需求也變得越來越迫切。同源策略給Web開發者帶來了很多困擾,他們不得不設法實現一些“合法”的跨域技術,由此誕生了jsop,iframe跨域等技巧。
W3C委員會決定製定一個新的標準--- CORS 來解決日益迫切的跨域訪問問題。
假設從 http://www.a.com/test.html 發起一個跨域的XMLHttpRequest 請求,請求的地址爲:http://www.b.com/test.php。
<script>
var client = new XMLHttpRequest();
client.open("GET", "http://www.b.com/test.php");
client.onreadystatechange = function(){ }
client.send(null);
</script>
如果服務器www.b.com返回一個HTTP Header:
Access-Control-Allow-Origin: http://www.a.com
代碼如下:
<?php
header("Access-Control-Allow-Origin: *");
?>
Cross Domain Request Test!
那麼這個來自 http://www.a.com/test.html 的跨域請求就會被通過。
在這個過程中,http://www.a.com/test.html 發起的請求還必須帶上一個 Origin Header:
Origin: http://www.a.com
在Firefox上,可以找包分析這個過程。
Origin Header 用於標記 HTTP 發起的”源“,服務器端通過識別瀏覽器自動帶上的這個Origin Header,來判斷瀏覽器的請求是否來自一個合法的”源“。Origin Header 可以用於防範CSRF,它不像Referer那麼容易被僞造或清空。
在上面的例子中,服務器返回:
Access-Control-Allow-Origin: *
從客戶端的跨域請求通過。在這裏使用了通配符 "*",這是極其危險的,它將允許來自任意域的請求訪問成功。這就好像Flash策略中的 allow-access-from: * 一樣,等於沒有做任何安全限制。
對於這個跨域訪問的標準,還有很多HTTP Header 可以用於進行更精確的控制:
PostMessage 跨窗口傳遞消息
在”跨站腳本攻擊“中,曾經提到利用window.name 來跨窗口、跨域傳遞消息。實際上,window這個對象幾乎是不受同源策略限制的,很多腳本攻擊都巧妙地利用了window對象的這一特點。
在HTML5中,爲了豐富Web開發者的能力,制定了一個新的API:postMessage。postMessage 允許每一個window(包括當前窗口、彈出窗口、iframes等)對象往其他的窗口發送文本消息,從而實現跨窗口的消息傳遞。這個功能不受同源策略限制的。
下面示例是一個postMessage的用法,
發送窗口:
<iframe src="http://dev.jquery.com/~john/message" id="iframe"></iframe>
<form id="form">
<input type="text" id="msg" value="Message to send"/>
<input type="submit"/>
</form>
<script>
window.onload = function(){
var win = document.getElementById("iframe").contentWindow;
document.getElementById("form").onsubmit = function(e){
win.postMessage(document.getElementById("msg").value);
e.preventDefault();
};
};
</script>
接收窗口:
<b>This iframe is loacated on dev.jquery.com</b>
<div id="test">Send me a message!</div>
<script>
document.addEventListener("message", function(e){
document.getElementById("test").textContent = e.domain + " said: " + e.data;
}, false);
</script>
在這個例子中,發送窗口負責發送消息;而在接收窗口中,需要綁定一個message事件,監聽其他窗口發來的消息。這是兩個窗口之間的一個”約定“,如果沒有監聽這個事件,則無法接收到消息。
在使用postMessage()時,有兩個安全問題需要注意。
(1)在必要時,可以在接收窗口驗證Domain,甚至驗證URL,以防來自非法頁面的消息。這實際上是在代碼中實現一次同源策略的驗證過程。
(2)在本例中,接收消息寫入textContent,但實際應用中,如果將消息寫入innerHTML,甚至直接寫入script中,則可能會導致DOM based XSS的產生。根據”Secure By Default“原則,在接收窗口不應該信任接收到的消息,而需要對消息進行安全檢查。
使用postMessage,也會使XSS payload變得更加的靈活。Gareth Heyes 曾經實現過一個JavaScript運行環境的sandbox,其原理是創建一個iframe,將JavaScript限制於其中執行。但筆者進過研究發現,利用postMessage()給父窗口發送消息,可以突破此sandbox。
Web Storage
過去在瀏覽器裏能夠存儲信息的方法有以下幾種:
- Cookie
- Flash Shared Object
- IE UserData
其中,Cookie主要用於保存登錄憑證和少量信息,其最大長度的限制決定了不可能在Cookie中存儲太多信息。而Flash Shared Object和IE UserData 則是Adobe與微軟自己的功能,並未成爲一個通用化的標準。因此W3C委員會希望能在客戶端有一個較爲強大和方便的本地存儲功能,這就是Web Storage。
Web Storage 分爲 Session Storage 和 Local Storage。Session Storage 關閉瀏覽器就會失效,而Local Storage則會一直存在。Web Storage就像一個非關係型數據庫,有Key-Value對組成,可以通過JavaScript對其進行操作。使用方法如下:
- 設置一個值:window.sessionStorage.setItem(key, value);
- 讀取一個值:window.sessionStorage.getItem(key);
下面這個示例展示了 Web Storage 的使用。
<div id="sessionStorage_show">
sessionStorage Value:
</div>
<br>
<div id="localStorage_show">
localStorage Value:
</div>
<input id="set" type="button" value="check" onclick="set();">
<script>
function set(){
window.sessionStorage.setItem("test", "this is sessionStorage");
if (window.globalStorage){
window.globalStorage.namedItem("a.com").setItem("test", "this is LocalStorage");
}else{
window.localStorage.setItem("test", "this is LocalStorage");
}
document.getElementById("sessionStorage_show").innerHTML += window.sessionStorage.getItem("test");
if(window.globalStorage){
document.getElementById("localStorage_show").innerHTML += window.glabolStorage.namedItem("a.com").getItem("test");
}else{
document.getElementById("localStorage_show").innerHTML += window.localStorage.getItem("test");
}
}
set();
</script>
運行結果如下:
Web Storage 也受到同源策略的約束,每個域所擁有的信息只會保存在自己的域下,如下例:
<body>
<script>
if (document.domain == "www.a.com"){
window.localStorage.setItem("test",123);
}
alert(window.localStorage.getItem("test"));
</script>
</body>
運行結果如下:
當域變化時,結果如下:
Web Storage讓Web開發更加的靈活多變,它的強大功能也爲XSS Payload大開方便之門。攻擊者可能將惡意代碼保存在Web Storage中,從而實現跨頁面攻擊。當Web Storage中保存有敏感信息時,也可能成爲攻擊的目標,而XSS攻擊可以完成這一過程。