http://www.91ri.org/tag/fuzz-bug
通常情況下,有三種方法被廣泛用來防御CSRF攻擊:驗證token,驗證HTTP請求的Referer,還有驗證XMLHttpRequests里的自定義header。鑒于種種原因,這三種方法都不是那么完美,各有利弊。
二?CSRF的分類
在跨站請求偽造(CSRF)攻擊里面,攻擊者通過用戶的瀏覽器來注入額外的網(wǎng)絡(luò)請求,來破壞一個網(wǎng)站會話的完整性。而瀏覽器的安全策略是允許當(dāng)前頁面發(fā)送到任何地址的請求,因此也就意味著當(dāng)用戶在瀏覽他/她無法控制的資源時,攻擊者可以控制頁面的內(nèi)容來控制瀏覽器發(fā)送它精心構(gòu)造的請求。
1、網(wǎng)絡(luò)連接。例如,如果攻擊者無法直接訪問防火墻內(nèi)的資源,他可以利用防火墻內(nèi)用戶的瀏覽器間接的對他所想訪問的資源發(fā)送網(wǎng)絡(luò)請求。甚至還有這樣一種情況,攻擊者為了繞過基于IP地址的驗證策略,利用受害者的IP地址來發(fā)起他想發(fā)起的請求。
2、獲知瀏覽器的狀態(tài)。當(dāng)瀏覽器發(fā)送請求時,通常情況下,網(wǎng)絡(luò)協(xié)議里包含了瀏覽器的狀態(tài)。這其中包括很多,比如cookie,客戶端證書或基于身份驗證的header。因此,當(dāng)攻擊者借助瀏覽器向需要上述這些cookie,證書和header等作驗證的站點發(fā)送請求的時候,站點則無法區(qū)分真實用戶和攻擊者。
3、改變?yōu)g覽器的狀態(tài)。當(dāng)攻擊者借助瀏覽器發(fā)起一個請求的時候,瀏覽器也會分析并相應(yīng)服務(wù)端的response。舉個例子,如果服務(wù)端的response里包含有一個Set-Cookie的header,瀏覽器會相應(yīng)這個Set-Cookie,并修改存儲在本地的cookie。這些改動都會導(dǎo)致很微妙的攻擊,我們將在第三部分描述。
作用范圍內(nèi)的威脅:我們按照產(chǎn)生危害的大小將此部分分成三種不同的危害模型。
1、論壇可交互的地方。很多網(wǎng)站,比如論壇允許用戶自定義有限種類的內(nèi)容。舉例來說,通常情況下,網(wǎng)站允許用戶提交一些被動的如圖像或鏈接等內(nèi)容。如果攻擊者讓圖像的url指向一個惡意的地址,那么本次網(wǎng)絡(luò)請求很有可能導(dǎo)致CSRF攻擊。這些地方都可以發(fā)起請求,但這些請求不能自定義HTTP?header,而且必須使用GET方法。盡管HTTP協(xié)議規(guī)范要求請求不能帶有危害,但是很多網(wǎng)站并不符合這一要求。
2、Web攻擊者。在這里web攻擊者的定義是指有自己的獨立域名的惡意代理,比如attacker.com,并且擁有attacker.com的HTTPS證書和web服務(wù)器。所有的這些功能只需要花10美元即可以做到。一旦用戶訪問attacker.com,攻擊者就可以同時用GET和POST方法發(fā)起跨站請求,即為CSRF攻擊。
3、網(wǎng)絡(luò)攻擊者。這里的網(wǎng)絡(luò)攻擊者指的是能控制用戶網(wǎng)絡(luò)連接的惡意代理。比如,攻擊者可以通過控制無線路由器或者DNS服務(wù)器來控制用戶的網(wǎng)絡(luò)連接。這種攻擊比web攻擊需要更多的資源和準(zhǔn)備,但我們認(rèn)為這對HTTPS站點也有威脅。因為HTTPS站點只能防護(hù)有源網(wǎng)絡(luò)。
作用范圍外的威脅:下面我們還列出了一些不在本論文討論范圍的相關(guān)危害模型。對這些危害的防御措施可以與CSRF的防御措施形成很好的互補(bǔ)。
1、跨站腳本(XSS)。如果攻擊者能夠向網(wǎng)站注入腳本,那么攻擊者就會破壞該網(wǎng)站用戶會話的完整性和保密性。有些XSS攻擊需要發(fā)起網(wǎng)絡(luò)請求,比如將用戶銀行賬戶里的錢轉(zhuǎn)移到攻擊者的賬戶里,但是通常情況下,對CSRF的防御并沒有考慮到這些情況。考慮到更安全的做法,網(wǎng)站必須實現(xiàn)對XSS和CSRF的同時防御。
2、惡意軟件。如果攻擊者能夠在用戶的電腦上運行惡意軟件,那么攻擊者就可以控制用戶的瀏覽器向那些可信的網(wǎng)站注入腳本。這時候基于瀏覽器的防御策略將會失效,因為攻擊者可以用含有惡意插件的瀏覽器來替換用戶的瀏覽器。
3、DNS的重新綁定。像CSRF一樣,DNS重新綁定可以使用用戶的IP地址來連接攻擊者指定的服務(wù)器。處在防火墻保護(hù)內(nèi)的服務(wù)器或者那些基于IP地址驗證的服務(wù)器需要一個對抗DNS重新綁定的防御方案。盡管DNS重新綁定的攻擊和CSRF攻擊的意圖非常相似,但是他們還是需要各自不同的解決方案。一個簡單的解決DNS重新綁定攻擊的方案就是要驗證主機(jī)的HTTP請求header,確保包含有預(yù)期值。還有一個替代方案就是過濾DNS流量,防止將外部的DNS名稱解析成內(nèi)部私有地址。
4、證書錯誤。如果用戶在出現(xiàn)HTTPS證書錯誤的時候還愿意繼續(xù)點擊訪問,那么HTTPS能夠提供的很多安全保護(hù)就沒有意義。有一些安全研究者指出了針對這一種情況的威害,但是在本文中,我們假設(shè)用戶不會在出現(xiàn)了HTTPS證書錯誤之后繼續(xù)點擊訪問。
5、釣魚。當(dāng)用戶在訪問釣魚網(wǎng)站的時候,在身份驗證的時候輸入個人信息,釣魚攻擊就發(fā)生了。釣魚攻擊現(xiàn)今非常普遍也很有效,因為用戶有的時候真的很難區(qū)分釣魚網(wǎng)站和真正的網(wǎng)站。
6、用戶跟蹤。一些合作網(wǎng)站會利用跨站請求來對用戶的瀏覽習(xí)慣建立一個關(guān)聯(lián)行為庫。大多數(shù)瀏覽器都通過組織第三方cookie發(fā)送來阻止類似的跟蹤,但是利用掛站請求,瀏覽器的這一特性可以被繞過。
三?登錄CSRF
無論是利用瀏覽器的網(wǎng)絡(luò)連接還是利用瀏覽器的狀態(tài),大多數(shù)對CSRF的討論都集中在能改變服務(wù)端狀態(tài)的請求上面。盡管CSRF攻擊能通過改變?yōu)g覽器的狀態(tài)來對用戶在訪問可信網(wǎng)站時候造成危害,但是對它的重視程度還是不夠。再登陸CSRF攻擊里面,攻擊者利用用戶在可信網(wǎng)站的用戶名和密碼來對網(wǎng)站發(fā)起一個偽造請求。一旦請求成功,服務(wù)器端就會響應(yīng)一個Set-Cookie的header,瀏覽器接收到以后就會建立一個session?cookie,并記錄用戶的登陸狀態(tài)。這個session?cookie被用作綁定后續(xù)的請求,因而也可被攻擊者用來作為身份驗證。依據(jù)不同的網(wǎng)站,登陸CSRF攻擊還可以造成很嚴(yán)重的后果。
搜索記錄:包括谷歌和雅虎等很多搜索引擎允許他們的用戶選擇是否同意保存他們的搜索記錄,并且為用戶提供一個接口來查看他們自己的私人搜索記錄。搜 索請求里面包含了用戶的行為習(xí)慣和興趣的一些敏感細(xì)節(jié),攻擊者可以利用這些細(xì)節(jié)來欺騙用戶,盜竊用戶的身份或者窺探用戶。當(dāng)攻擊者以用戶身份登陸到搜索引 擎里,就可以看到用戶的搜索記錄。如圖1.?這樣,用戶的搜索查詢記錄就被存儲到了攻擊者的搜索記錄里,攻擊者就可以登陸自己的賬戶隨便查詢用戶的搜索記錄。
圖1.?登陸CSRF攻擊事件的跟蹤圖。受害人訪問攻擊者的網(wǎng)站,攻擊者向谷歌偽造一個跨站點請求的登陸框,造成受害者被攻擊者登陸到谷歌。隨后,受害者使用搜索的時候,搜索記錄就被攻擊者記錄下來。
PayPal:PayPal允許它的用戶相互之間任意轉(zhuǎn)移資金。轉(zhuǎn)移資金的時候,用戶要注冊信用卡或者銀行賬戶。攻擊者可以利用登陸CSRF來發(fā)起以下攻擊:
1、受害者訪問了惡意商家的網(wǎng)站,并選擇使用PayPal支付。
2、受害者被重定向到PayPal并且要求登陸他/她的賬戶。
3、網(wǎng)站等待用戶登陸他/她的PayPal賬戶。
4、付款的時候,受害者先是登記自己的信用卡,但是信用卡實際上已經(jīng)被添加到惡意商家的PayPal賬戶。
iGoogle:用戶可以通過使用iGoogle來定制自己的谷歌主頁,也包括一些插件。為了易用性,這些插件是“嵌入到iGoogle的”,這也就意味著他們將影響到iGoogle的安全。通常情況下,iGoogle在添加新插件的時候,都會詢問用戶做出信任決定。但是攻擊者可以通過登錄CSRF攻擊來幫助用戶做出決定,從而安裝任意的插件。
1、攻擊者通過用戶的瀏覽器授權(quán)安裝一個iGoogle插件(含有惡意腳本),并將插件添加到用戶的定制化iGoogle主頁。
2、攻擊者使用戶登陸谷歌,并開一個到iGoogle的框架。
3、谷歌認(rèn)為受害者就是攻擊者,并將攻擊者的插件推送給受害者,而且允許攻擊者在https://www.google.com域下運行腳本。
4、攻擊者現(xiàn)在可以:(a)在正確的URL頁面構(gòu)造一個登陸框(b)盜取用戶自動填充的密碼(c)在另一個窗口等待用戶登陸并讀取document.cookie。
我們已經(jīng)將上述漏洞告知了谷歌,他們已經(jīng)在兩方面來減緩漏洞帶來的危害。首先,谷歌已經(jīng)棄用內(nèi)嵌的插件并禁止開發(fā)者開發(fā)類似的插件,只允許少部分比較受歡迎的內(nèi)嵌插件。其次,谷歌已經(jīng)開發(fā)了私密token策略來防御登陸CSRF(下面將會討論),但是這個策略只對登陸了的用戶才有效。我們預(yù)計,谷歌一旦充分測試了他們的防御方案并覺得有效之后,會否認(rèn)他們的登陸CSRF漏洞。
四?現(xiàn)有的CSRF防御方案
一般網(wǎng)站有三種防御CSRF攻擊的方案。(1)驗證token值。(2)驗證HTTP頭的Referer。(3)用XMLHttpRequest附加在header里。以上三種方法都在廣泛使用,但是他們的效果都不是那么的令人滿意。
4.2?Token驗證
在每個HTTP請求里附加一部分信息是一個防御CSRF攻擊的很好的方法,因為這樣可以判斷請求是否已經(jīng)授權(quán)。這個“驗證token”應(yīng)該不能輕易的被未登錄的用戶猜測出來。如果請求里面沒有這個驗證token或者token不能匹配的話,服務(wù)器應(yīng)該拒絕這個請求。
Token驗證的方法可以用來防御登陸CSRF,但是開發(fā)者往往會忘記驗證,因為如果沒有登陸,就不能通過session來綁定CSRF?token。網(wǎng)站要想用驗證token的方式來防御登陸CSRF攻擊的話,就必須先創(chuàng)建一個“前session”,這樣才能部署CSRF的防御方案,在驗證通過了之后,再創(chuàng)建一個真正的session。
Token的設(shè)計。有很多技術(shù)可以生成驗證token。
??session標(biāo)識符。瀏覽器的cookie存儲方式就是為了防止不同域之間互相訪問cookie。一個普遍的做法是直接利用用戶的session標(biāo)識符來作為驗證token。服務(wù)器在處理每一個請求時,都將用戶的token與session標(biāo)識符來匹配。如果攻擊者能夠猜測出用戶的token,那么他就能登錄用戶的賬戶。而且這樣做有個不好的地方在于,偶爾用戶正在瀏覽的內(nèi)容會發(fā)送給第三方,比如通過電子郵件直接上網(wǎng)頁內(nèi)容上傳到瀏覽器廠商的bug跟蹤數(shù)據(jù)庫。如果正好這個頁面包含有用戶的session標(biāo)識符,任何能看到這個頁面的人都能模擬用戶登陸到網(wǎng)站,直到會話過期。
??獨立session隨機(jī)數(shù)。與直接使用用戶的session標(biāo)識符不一樣的是,當(dāng)用戶第一次登陸網(wǎng)站的時候,服務(wù)器可以產(chǎn)生一個隨機(jī)數(shù)并將它存儲在用戶的cookie里面。對于每一個請求,服務(wù)器都會將token與存儲在cookie里的值匹配。例如,廣泛使用的Trac問題跟蹤系統(tǒng)就是用的此技術(shù)。但是這個方法不能防御主動的網(wǎng)絡(luò)攻擊,即使是整個web應(yīng)用都使用的是HTTPS協(xié)議。因為攻擊者可以使用他自己的CSRF?token來覆蓋來覆蓋這個獨立session隨機(jī)數(shù),進(jìn)而可以使用一個匹配的token來偽造一個跨站請求。
??依賴session隨機(jī)數(shù)。有一個改進(jìn)產(chǎn)生隨機(jī)數(shù)的方法是將用戶的session標(biāo)識符與CSRF?token建立對應(yīng)關(guān)系后存儲在服務(wù)端。服務(wù)器在處理請求的時候,驗證請求中的token是否與session標(biāo)識符匹配。這個方法有個不好的地方就是服務(wù)端必須要維護(hù)一個很大的對應(yīng)關(guān)系表(哈希表)。
??session標(biāo)識符的HMAC。有一種方法不需要服務(wù)端來維護(hù)哈希表,就是可以對用戶的session?token做一個加密后用作CSRF?的token。例如,?Ruby?on?Rails的web程序一般都是使用的這種方法,而且他們是使用session標(biāo)識符的HMAC來作為CSRF?token的。只要所有的網(wǎng)站服務(wù)器都共享了HMAC密鑰,那么每個服務(wù)器都可以驗證請求里的CSRF?token?是否與session標(biāo)識符匹配。HMAC的特性能確保即使攻擊者知道用戶的CSRF?token,也不能推斷出用戶的session標(biāo)識符。
鑒于有充足的資源,網(wǎng)站都可以使用HMAC方法來防御CSRF攻擊。但是,很多網(wǎng)站和一些CSRF的防御框架(比如NoForge,?CSRFx?和CSRFGuard)都不能正確的實現(xiàn)比較隱秘的token防御。一個常見的錯誤就是在處理跨站請求的時候暴露了CSRF?token。舉個例子,一個可信的網(wǎng)站在對另一個網(wǎng)站發(fā)起請求的時候附加上了CSRF?token,那么那個網(wǎng)站就可以對這個可信的網(wǎng)站偽造一個跨站請求。
案例研究:NoForge.NoForge就是使用服務(wù)端保存哈希表的方式來驗證用戶的CSRF?token。它在所有鏈接和表單提交的時候會附加一個CSRF?token,造成這種技術(shù)不太完善的原因有以下三個:
1、HTML是在瀏覽器里動態(tài)創(chuàng)建的,而不會被重新加上CSRF?token。有些網(wǎng)站是在客戶端創(chuàng)建HTML的。比如Gmail,?Flickr,?和?Digg都是用JavaScript?來創(chuàng)建表單,而這些表單正是需要CSRF防御措施的。
2、NoForge并沒有對指向本站和外站的超鏈接作區(qū)分。如果有一個指向外站的超鏈接,那么外站可以用請求里面獲取到用戶的CSRF?token。比如,如果phpBB部署了NoForge,那么一旦用戶點擊了一個連接,連接的站點就可以獲取到用戶的CSRF?token,即使NoForge區(qū)分了是本站的鏈接還是外站的鏈接,因為Referer?還是會暴露用戶的CSRF?token。
3、NoForge對登陸CSRF并沒有什么效果,因為如果用戶已經(jīng)有了session標(biāo)識符(登陸了),那么NoForge只會驗證CSRF?token。盡管這種缺陷是可以修復(fù),但是這也說明了要想正確的實施token驗證策略并不是一件很容易的事情。
雖然上述三個原因都是可以修復(fù)的,但是這些缺陷都說明了要想正確地實施token驗證策略,是很復(fù)雜的一件事情。CSRFx?和?CSRFGuard,還有很多網(wǎng)站都說明了這一問題。
4.2?Referer
大多數(shù)情況下,當(dāng)瀏覽器發(fā)起一個HTTP請求,其中的Referer標(biāo)識了請求是從哪里發(fā)起的。如果HTTP頭里包含有Referer的時候,我們可以區(qū)分請求是同域下還是跨站發(fā)起的,因為Referer離標(biāo)明了發(fā)起請求的URL。網(wǎng)站也可以通過判斷有問題的請求是否是同域下發(fā)起的來防御CSRF攻擊。
不幸的是,通常Referer會包含有一些敏感信息,可能會侵犯用戶的隱私。比如,Referer可以顯示用戶對某個私密網(wǎng)站的搜索和查詢。盡管這 些內(nèi)容對私密網(wǎng)站站長來說是好事,因為他們可以通過這些內(nèi)容來優(yōu)化搜索引擎排名,但是一些用戶還是認(rèn)為侵犯了他們的隱私。另外,許多組織也很擔(dān)憂 Referer可能會將內(nèi)網(wǎng)的一些機(jī)密信息泄露出去。
漏洞。從歷史上來看,瀏覽器的一些漏洞使得一些惡意網(wǎng)站有欺騙Referer的價值,尤其是在使用代理服務(wù)器的時候。很多對Referer欺騙的討論都標(biāo)明瀏覽器允許Referer可以偽造。Mozilla在Fire-fox?1.0.7里面已經(jīng)修復(fù)了Referer欺騙的漏洞。目前的IE則還有這方面的漏洞,但是這些漏洞只能影響XMLHttpRequest,并且只能用來偽造Referer跳轉(zhuǎn)到攻擊者自己的網(wǎng)站。
尺度。如果網(wǎng)站選擇使用Referer來防御CSRF攻擊的話,那么網(wǎng)站的開發(fā)人員就需要決定到底是使用比較寬松還是比較嚴(yán)格的Referer驗證策略。如果采用寬松的Referer驗證策略,網(wǎng)站就應(yīng)該阻止Referer值不對的請求。如果請求里面沒有Referer,就接收請求。盡管這個方法用的很普遍,但是它很容易被繞過。因為攻擊者可以在header里面去掉Referer。例如,F(xiàn)TP和數(shù)據(jù)URL發(fā)起的請求里面就不包含Referer。 如果使用嚴(yán)格的Referer驗證策略,網(wǎng)站還要阻止沒有Referer的請求。這樣做主要是為了防止惡意網(wǎng)站主動隱藏Referer,但也會帶來兼容性 問題,比如會誤殺一部分合法的請求,因為有些瀏覽器和網(wǎng)絡(luò)的設(shè)置默認(rèn)就是不含有Referer的。所以說這個度一定要掌握好,很多時候取決于經(jīng)驗。我們還 會在4.2.1里討論這個問題。
個案研究:Facebook。縱觀Facebook的大部分網(wǎng)站都是使用token認(rèn)證的方式來防御CSRF攻擊的。但是,在Facebook的登陸框部分則使用的是寬松的Referer驗證策略。這種方法在面對登陸CSRF的攻擊時沒有什么作用。舉例來說,攻擊者可以講用戶從http://attacker.com/重定向到ftp://attacker.com/index.html?,然后再對Facebook發(fā)起一個跨站的登陸請求。因為請求來自FTP?URL,所以大多數(shù)瀏覽器都不會在請求里包含Referer。
4.2.1?實驗
為了評估嚴(yán)格的Referer驗證策略的兼容性,我們進(jìn)行了一項實驗來衡量到底有多大概率以及在什么情況下,合法的請求里面不含有Referer。
設(shè)計。廣告是一個很方便測量瀏覽器和網(wǎng)絡(luò)特征的渠道,因此我們可以利用廣告作為實驗平臺。在2008年4月5日到4月8日期間,我們從163,767個獨立IP購買了283,945?個廣告,分別是兩個不同的廣告渠道。在渠道A,我們以每千次展示0.50美元的價格購買了網(wǎng)絡(luò)旗幟廣告,關(guān)鍵字為“火狐”,“游戲”,“IE”,“視頻”,“YouTube”。在渠道B,我們以每千次展示5美元的價格的間隙廣告,關(guān)鍵字為“芭蕾”,“金融“,“花”,“食品”和“園藝”。我們在每個廣告渠道上花了100美元,渠道A有241,483點擊量(146,310個獨立IP),渠道B有42,406點擊量(18,314個獨立IP)。
廣告服務(wù)是由我們實驗室里的兩臺主機(jī)提供,兩個獨立的域名是從不同的注冊商處購買。每當(dāng)顯示廣告時,廣告會在接下來的每個請求里面生成一個特定的標(biāo)識符,并隨機(jī)選擇一臺主機(jī)作為主服務(wù)器。主服務(wù)器通過HTTP或者HTTPS協(xié)議將客戶端HTML發(fā)送到我們的服務(wù)器,這些HTML能發(fā)起一個GET或者POST請求。其中,請求包括提交表單,圖像請求和XMLHttpRequests。請求的順序是隨機(jī)的并且跟用戶的操作無關(guān)。當(dāng)廣告通過了瀏覽器的安全策略之后,就向主服務(wù)器發(fā)起一個同域的請求,同時向次服務(wù)器發(fā)起一個跨域請求。每個服務(wù)器的成本是400美元,域名是7美元,從一個合法的證書頒發(fā)機(jī)構(gòu)獲得的90天域驗證的HTTPS證書是免費的。服務(wù)器根據(jù)接收到的網(wǎng)絡(luò)請求來記錄請求參數(shù),包括Referer,User-Agent頭,日期,客戶端的C類網(wǎng)絡(luò),會話標(biāo)識符。服務(wù)器還通過DOM?API記錄了document.referrer的值,但是不記錄客戶端的IP地址。為了統(tǒng)計獨立的IP地址,服務(wù)器利用一個隨機(jī)產(chǎn)生的KEY而不是記錄HMAC的方式,這個KEY會被丟棄。服務(wù)器記錄的信息不足以單獨確定廣告的瀏覽者到底有多少。
倫理。實驗的設(shè)計遵守兩個廣告渠道的規(guī)則。實驗中的行為基本上都是web廣告每天的行為,所以都能正常的從廣告商那里請求額外的資源,比如圖片,音頻和視頻。盡管我們的廣告產(chǎn)生的HTTP請求數(shù)目遠(yuǎn)大于普通的廣告,但是我們需要的帶寬明顯比一個視頻廣告需要的帶寬要小。我們的服務(wù)器也像廣告商一樣,只記錄他們所記錄的信息。實際上我們的服務(wù)器記錄的信息明顯要比商業(yè)的廣告商要少,因為我們并不記錄客戶端的IP地址。
結(jié)果。我們已經(jīng)將結(jié)果在圖2和圖3里總結(jié)出來了,我們還發(fā)現(xiàn)以下結(jié)果只有95%的可信度。
??HTTP方法里,?跨域請求比同域請求不包含Referer頭的情況更普遍,而在POST方法(卡方系數(shù)=?2130,?p值<0.001)?和GET方法(卡方系數(shù)=?2175,?p值<0.001)?里比較,前者不包含Referer頭的情況更為普遍。
??在不包含Referer頭的統(tǒng)計中,HTTP比HTTPS更為普遍,包括跨域POST(卡方系數(shù)=?6754,?p值<0.001)請求,跨域GET(卡方系數(shù)=?6940,?p值<0.001)請求,同域POST(卡方系數(shù)=?2286,?p值<0.001)請求和同域GET請求(卡方系數(shù)=?2377,?p值<0.001)。
??在不包含Referer頭的統(tǒng)計中,廣告渠道B所有形式的請求都比A要更普遍。這些請求形式包括:HTTP跨域POST(卡方系數(shù)=?3060,?p值<0.001),HTTP同域POST(卡方系數(shù)=?6537,?p值<0.001),HTTPS跨域POST(卡方系數(shù)=?49.13,?p值<0.001)和HTTPS同域POST(卡方系數(shù)=?44.52,?p值<0.001)請求。
??我們還統(tǒng)計了自定義的header?X-Requested-By(參見4.3節(jié))和Origin(見第5章),X-Requested-By大概有0.029%到0.047%的HTTP?POST請求,0.084%到0.112%的HTTP?GET請求,0.008%到0.018%的HTTPS?POST請求和?0.009%到0.020%的HTTPS?GET請求里不包含有Referer頭。Origin則在與上述相同的請求里都不包含Referer頭。
圖2.?不包含Referer和Referer不正確的請求(283,945?個結(jié)果)。x和y分別代表主服務(wù)器和次服務(wù)器的域名
討論。下面有兩個有力的證據(jù)可以表明在不包含Referer的請求里,通常是來自網(wǎng)絡(luò)(攻擊)而不是瀏覽器。
1、HTTP請求比HTTPS請求不包含Referer更為普遍是因為,網(wǎng)絡(luò)代理可以刪除HTTP請求里的header,但是不能刪除HTTPS請求里的header。當(dāng)然,在一些企業(yè)的網(wǎng)絡(luò)里,一些HTTPS的終端就是一個網(wǎng)絡(luò)代理,這種情況下代理可以修改HTTPS請求,但是這種情況是比較罕見的。
2、瀏覽器在去掉Referer的時候也會去掉document.referrer的值,但是如果Referer是在網(wǎng)絡(luò)里去掉的話,document.referrer卻還在。但是我們發(fā)現(xiàn),Referer去掉的情況比document.referrer去掉的情況要更為普遍。
實際上,在實驗中,document.referrer值被去掉主要是因為兩種特殊的瀏覽器:PlayStation?3?瀏覽器不支持document.referrer,Opera去掉document.referrer(但是并不去掉Referer)是為了跨站HTTPS請求。XMLHttpRequest中的Referer被去掉的比例較高是由于Firefox?1.0和1.5中的bug引起的。所有的這些結(jié)果都表明只有極少數(shù)的瀏覽器被配置成不發(fā)送Referer。
也有證據(jù)表明,Referer被去掉是由于涉及到隱私問題,當(dāng)瀏覽器把Referer從網(wǎng)站A發(fā)送到網(wǎng)站B時,用戶的隱私也在被暴露,因為網(wǎng)站B可以通過Referer來收集用戶在網(wǎng)站A的瀏覽行為。相比之下,在同域下發(fā)送Referer則不會引起隱私問題,因為網(wǎng)站完全可以通過cookie來收集用戶的隱私(也就是完全沒有必要通過Referer來收集)。我們還發(fā)現(xiàn),跨站請求比同站請求要更多的阻止Referer,說明由于考慮到隱私的問題,所以才會人為的阻止Referer發(fā)送。
由此,我們得出兩個主要的結(jié)論:
1、通過HTTPS來防御CSRF。在HTTPS請求里,Referer可以被用來防御CSRF。為了實施用Referer來防御CSRF的策略,網(wǎng)站必須拒絕那些沒有Referer的請求,因為攻擊者可以控制瀏覽器來去掉Referer。而在HTTP里,網(wǎng)站則不能一味的拒絕沒有Referer的請求,因為考慮到兼容性,可能有相當(dāng)大一部分?(大約?3–11%)用戶可能就訪問不了網(wǎng)站了。不同的是在HTTPS里,則可以執(zhí)行嚴(yán)格的Referer驗證策略,因為只有很小的一部分(0.05–0.22%)瀏覽器會去掉Referer。特別需要指出的是,嚴(yán)格的Referer驗證策略非常適合用來防御登陸CSRF,因為通常情況下,登陸請求都是通過HTTPS協(xié)議發(fā)起的。
2、隱私問題。嚴(yán)格的Referer策略是很好的CSRF的防御方案,因為它實施起來很簡單。不幸的是,隱私策略可能會阻止此方案的流行。因此,瀏覽器新的安全性能和新的CSRF防御機(jī)制都必須要先解決好隱私問題,才能大規(guī)模的部署。
圖3.?廣告渠道A中不包含Referer和Referer不正確的請求(241,483?個結(jié)果)。Opera阻止了跨站的HTTPS?document.referrer,F(xiàn)irefox?1.0和1.5由于bug在XMLHttpRequest的時候不發(fā)送Referer,PlayStation?3(圖中即為PS)不支持document.referrer。
4.3?自定義HTTP?header
我們也可以用自定義HTTP頭的方法來防御CSRF攻擊,因為雖然瀏覽器會阻止向外站發(fā)送自定義的HTTP頭,但是允許向本站通過XMLHttpRequest的方式發(fā)送自定義HTTP頭。比如,prototype.js這個JavaScript庫就是使用這種方法,并且增加了?X-Requested-By頭到XMLHttpRequest里面?。Google?Web?Toolkit?也建議開發(fā)者用在XMLHttpRequest里增加一個X-XSRF-Cookie頭的方法來防御CSRF攻擊,其中XMLHttpRequets包含有cookie的值。當(dāng)然XMLHttpRequets里面的cookie并不需要用來防御CSRF,因為只需要有頭部分就足夠了。
在使用這種方法來防御CSRF攻擊的時候,網(wǎng)站必須在所有的請求里使用XMLHttpRequest并附加一個自定義頭(比如X-Requested-By),并且拒絕所有沒有自定義頭的的請求。例如,為了防御登陸CSRF的攻擊,網(wǎng)站必須通過XMLHttpRequest的方式發(fā)送用戶的身份驗證信息到服務(wù)器。在我們的實驗里,在服務(wù)器接收到的請求里面,大約有99.90–99.99%的請求是含有X-Requested-By頭的,這表明這一方法適用于絕大多數(shù)的用戶。
五?建議:Origin字段
為了防止CSRF的攻擊,我們建議修改瀏覽器在發(fā)送POST請求的時候加上一個Origin字段,這個Origin字段主要是用來標(biāo)識出最初請求是從哪里發(fā)起的。如果瀏覽器不能確定源在哪里,那么在發(fā)送的請求里面Origin字段的值就為空。
隱私方面:這種Origin字段的方式比Referer更人性化,因為它尊重了用戶的隱私。
1、Origin字段里只包含是誰發(fā)起的請求,并沒有其他信息?(通常情況下是方案,主機(jī)和活動文檔URL的端口)。跟Referer不一樣的是,Origin字段并沒有包含涉及到用戶隱私的URL路徑和請求內(nèi)容,這個尤其重要。
2、Origin字段只存在于POST請求,而Referer則存在于所有類型的請求。
隨便點擊一個超鏈接(比如從搜索列表里或者企業(yè)intranet),并不會發(fā)送Origin字段,這樣可以防止敏感信息的以外泄露。
在應(yīng)對隱私問題方面,Origin字段的方法可能更能迎合用戶的口味。
服務(wù)端要做的:用Origin字段的方法來防御CSRF攻擊的時候,網(wǎng)站需要做到以下幾點:
1、在所有能改變狀態(tài)的請求里,包括登陸請求,都必須使用POST方法。對于一些特定的能改變狀態(tài)的GET請求必須要拒絕,這是為了對抗上文中提到過的論壇張貼的那種危害類型。
2、對于那些有Origin字段但是值并不是我們希望的(包括值為空)請求,服務(wù)器要一律拒絕。比如,服務(wù)器可以拒絕一切Origin字段為外站的請求。
安全性分析:雖然Origin字段的設(shè)計非常簡單,但是用它來防御CSRF攻擊可以起到很好的作用。
1、去掉Origin字段。由于支持這種方法的瀏覽器在每次POST請求的時候都會帶上源header,那么網(wǎng)站就可以通過查看是否存在這種Origin字段來確定請求是否是由支持這種方法的瀏覽器發(fā)起的。這種設(shè)計能有效防止攻擊者將一個支持這種方法的瀏覽器改變成不支持這種方法的瀏覽器,因為即使你改變?yōu)g覽器去掉了Origin字段,Origin字段還是存在,只不過值變?yōu)榭樟恕_@跟Referer很不一樣,因為Referer?只要是在請求里去掉了,那服務(wù)器就探測不到了。
2、DNS重新綁定。在現(xiàn)有的瀏覽器里面,對于同站的XMLHttpRequests,Origin字段可以被偽造。只依賴網(wǎng)絡(luò)連接進(jìn)行身份驗證的網(wǎng)站應(yīng)當(dāng)使用在第2章里提到的DNS重新綁定的方法,比如驗證header里的Host字段。在使用Origin字段來防御CSRF攻擊的時候,也需要用到DNS重新綁定的方法,他們是相輔相成的。當(dāng)然對于在第四章里提到的CSRF防御方法,也需要用到DNS重新綁定的方法。
3、插件。如果網(wǎng)站根據(jù)crossdomain.xml準(zhǔn)備接受一個跨站HTTP請求的時候,攻擊者可以在請求里用Flash?Player來設(shè)置Origin字段。在處理跨站請求的時候,token驗證的方法處理的不好,因為token會暴露。為了應(yīng)對這些攻擊,網(wǎng)站不應(yīng)當(dāng)接受不可信來源的跨站請求。
4、應(yīng)用。Origin字段跟以下四個用來確定請求來源的建議非常類似。Origin字段以下四個建議的基礎(chǔ)上統(tǒng)一并改進(jìn)了,目前已經(jīng)有幾個組織采用了Origin字段的方法建議。
??Cross-Site?XMLHttp?Request。Cross-Site?XMLHttp?Request的方法規(guī)定了一個Access-Control-Origin?字段,用來確定請求來源。這個字段存在于所有的HTTP方法,但是它只在XMLHttpRequests請求的時候才會帶上。我們對Origin字段的設(shè)想就是來源于這個建議,而且Cross-Site?XMLHttp?Request工作組已經(jīng)接受我們的建議愿意將字段統(tǒng)一命名為Origin。
?XDomainRequest。在Internet?Explorer?8?Beta?1里有XDomainRequest的API,它在發(fā)送HTTP請求的時候?qū)eferer里的路徑和請求內(nèi)容刪掉了。被縮減后的Referer字段可以標(biāo)識請求的來源。我們的實驗結(jié)果表明這種刪減的Referer字段經(jīng)常會被拒絕,而我們的Origin字段卻不會。微軟已經(jīng)發(fā)表聲明將會采用我們的建議將XDomainRequest里的刪減Referer更改為Origin字段。
??JSONRequest。在JSONRequest這種設(shè)計里,包含有一個Domain字段用來標(biāo)識發(fā)起請求的主機(jī)名。相比之下,我們的Origin字段方法不僅包含有主機(jī),還包含請求的方案和端口。JSONRequest規(guī)范的設(shè)計者已經(jīng)接受我們的建議愿意將Domain字段更改為Origin字段,以用來防止網(wǎng)絡(luò)攻擊。
??Cross-Document?Messaging。在HTML5規(guī)范里提出了一個建議,就是建立一個新的瀏覽器API,用來驗證客戶端在HTML文件之間鏈接。這種設(shè)計里面包含一個不能被覆蓋的origin屬性,如果不是在客戶端的話,在服務(wù)端驗證這種origin屬性的過程與我們驗證origin字段的過程其實是一樣的。
具體實施:我們在服務(wù)器和瀏覽器端都實現(xiàn)了利用origin字段的方法來防止CSRF攻擊。在瀏覽器端我們的實現(xiàn)origin字段方式是,在WebKit添加一個8行代碼的補(bǔ)丁,Safari里有我們的開源組件,F(xiàn)irefox里有一個466行代碼的插件。在服務(wù)器端我們實現(xiàn)origin字段的方式是,在ModSecurity應(yīng)用防火墻里我們只用3行代碼,在Apache里添加一個應(yīng)用防火墻語言(見圖4)。這些規(guī)則在POST請求里能驗證Host字段和具有合法值的origin字段。在實現(xiàn)這些規(guī)則來防御CSRF攻擊的時候,網(wǎng)站并不需要做出什么改變,而且這些規(guī)則還能確保GET請求沒有任何攻擊性(前提是瀏覽器端已經(jīng)實現(xiàn)了origin字段方法)。
圖4.?在ModSecurity里實現(xiàn)origin字段方法來防御CSRF攻擊
六?session初始化
在session初始化的時候,登陸CSRF只是其中一個很普遍的漏洞。在session初始化了之后,web服務(wù)器通常會將用戶的身份與session標(biāo)識符綁定起來。因此有兩種類型的session初始化漏洞,一種是服務(wù)器將可信用戶的身份與新初始化的session綁定到了一起,另一種是服務(wù)器將攻擊者的身份與session綁定到了一起。
??作為可信用戶的驗證。在某些特定的情況下,攻擊者可以使用一個可預(yù)見的session標(biāo)識符強(qiáng)制網(wǎng)站開啟一個新的session。這一類型的漏洞一般都被稱為session定位漏洞。當(dāng)用戶提供他們的身份信息給一個可信網(wǎng)站來驗證后,網(wǎng)站會將用戶的身份與一個可預(yù)見的session標(biāo)識符綁定到一起。攻擊者此時就可以通過這個session標(biāo)識符來扮演用戶的身份登錄網(wǎng)站。
??作為攻擊者的驗證。攻擊者也可以通過用戶的瀏覽器強(qiáng)制網(wǎng)站開始一個新的session,并且強(qiáng)制session與攻擊者的身份綁定到一起(第3章已經(jīng)說明了攻擊是怎么完成的)。登錄CSRF攻擊只是這一類型中的最簡單漏洞,但是攻擊者還可以有其他的方法強(qiáng)制通過用戶的瀏覽器將session與自己綁定到一起。
6.1?HTTP請求
OpenID:像LiveJournal、Movable?Type和WordPress等很多網(wǎng)站都在使用OpenID?協(xié)議,建議這些可以使用自簽名隨機(jī)數(shù)的方式來對抗回復(fù)攻擊,但不要將OpenID?session與用戶的瀏覽器綁定到一起,因為攻擊者可以強(qiáng)制用戶的瀏覽器初始化一個session然后將session與自己綁定到一起。規(guī)范中聲明了:?return_to?這個URL可能被委托方用來在用戶的驗證請求與驗證答復(fù)之間建立聯(lián)系。但是LiveJournal,?Movable?Type和WordPress都認(rèn)為這不是必須的,也沒有實施它。為了對抗這種攻擊,在協(xié)議初始化的時候委托方應(yīng)該生成一個新的隨機(jī)數(shù),并將它與瀏覽器的cookie存儲到一起,將它包含到return_to參數(shù)里。委托方會將在cookie里的隨機(jī)數(shù)與return_to參數(shù)里的隨機(jī)數(shù)匹配。這種方法其實與token驗證的方法很類似,并且確保了從一開始OpenID?協(xié)議的session就能在同一個瀏覽器上完成。
PHP?cookieless(不用cookie的)驗證:這種方法被Hushmail?等網(wǎng)站用來防止用戶的電腦上還保留有cookie。Cookieless?驗證方法是將用戶的session標(biāo)識符存儲在請求的參數(shù)里面。但是這個方法不能將session與用戶的瀏覽器綁定到一起,因此攻擊者可以強(qiáng)制用戶的瀏覽器初始化一個session與攻擊者綁定到一起。為了防止這種攻擊,網(wǎng)站必須使用另外的方法將session標(biāo)識符與用戶的瀏覽器綁定到一起。例如,網(wǎng)站可以構(gòu)造一個長時間的frame,其中包含有session標(biāo)識符。這種方式是通過將session標(biāo)識符保存在內(nèi)存里來將用戶的瀏覽器與session綁定。使用PHP?cookieless驗證方法的網(wǎng)站通常也會存在session初始化漏洞,會讓攻擊者可以模仿一個可信的用戶。當(dāng)然,類似的session定位漏洞有很多標(biāo)準(zhǔn)的防御方法,例如,當(dāng)用戶登陸后,網(wǎng)站可以再次生成一個session標(biāo)識符。
6.2?Cookie重寫
漏洞。服務(wù)器可以在Set-Cookie字段里用一個Secure?flag方式告訴瀏覽器此cookie只能通過HTTPS協(xié)議發(fā)送。現(xiàn)金的瀏覽器都支持這個特性,并且在一些對安全性要求比較高的網(wǎng)站,這個特性通常被用來保護(hù)session。但是,這個Secure?flag并不能保證完整性。攻擊者可以模仿網(wǎng)站通過HTTP向同一個主機(jī)發(fā)送Set-Cookie字段,并在主機(jī)上設(shè)立了cookie。當(dāng)瀏覽器通過HTTPS向網(wǎng)站發(fā)送cookie的時候,網(wǎng)站并沒有一個機(jī)制來確定cookie是否被攻擊者重寫。如果這個cookie里面包含有用戶的session標(biāo)識符,攻擊者就可以很容易的通過重寫用戶的cookie來發(fā)起一個session初始化攻擊。基本上沒有網(wǎng)站能夠防御這種攻擊,因為他們需要客戶端提供一個cookie來作完整性驗證。但是,有人建議使用瀏覽器的特性,比如localStorage,它可以彌補(bǔ)這一不足。換句話說,如果網(wǎng)站聲稱它的應(yīng)用層session的驗證完全跟基于cookie的HTTP層的session無關(guān)的話,攻擊者可以在驗證之前就重寫用戶的cookie,然后扮演用戶登陸網(wǎng)站。盡管安全人員很多年前就知道攻擊者可以重寫cookie,但是瀏覽器廠商并沒有什么好的對抗辦法。廠商考慮到了通過拒絕HTTP請求的方式來對抗cookie重寫的攻擊,但是這一做法顯然不太合理。更糟糕的是,這一方法并不能提供cookie的完整性,因為Cookie?字段本身并不能區(qū)分cookie?里是否含有Secure?flag。
防御方法。為了不改變現(xiàn)有的cookie字段而就能保護(hù)cookie的完整性(是否包含有Secure?flag),我們建議瀏覽器可以在HTTPS請求里面新加一個Cookie-Integrity字段,專門用來檢測cookie的完整性狀態(tài)。這樣也是考慮了兼容以前策略的做法。例如
Cookie:?SID=DQAAAHQA…;?pref=ac81a9…;?TM=1203…
Cookie-Integrity:?0,?2
當(dāng)cookie被設(shè)置成使用HTTPS協(xié)議發(fā)送的時候,Cookie-Integrity字段可以在請求里面用來描述cookie字段的索引。如果請求里面的cookie都沒有被設(shè)置成HTTPS,那么Cookie-Integrity字段的值就為空。對Cookie-Integrity字段的完整性的保護(hù)與Secure?flag能提供的機(jī)密是相輔相成的,并且這樣做也具備很好的兼容性,因為服務(wù)器會忽略具有無法識別的header的請求。下面是幾個設(shè)計的建議:
帶寬。在每一個HTTP請求中添加內(nèi)容必然會增加所有網(wǎng)絡(luò)的延遲,為了節(jié)省帶寬,我們只在cookie字段里添加cookie的索引值。還有一個建議做法就是添加一個類似cookie字段的副本,命名為cookie2。
多樣性。當(dāng)主機(jī)準(zhǔn)備建立一個與已有cookie同名的cookie,那么cookie完全可以包含兩個同名的cookie。因為在此種情況下,也許Cookie-Integrity字段不能根據(jù)cookie名來分辨它們,但是我們可以在cookie字段里面通過索引值來區(qū)別它們。
Rollback。在HTTPS請求里面加入Cookie-Integrity字段可以有效的防止rollback攻擊。?如果沒有Cookie-Integrity字段,并且在不能保證cookie完整性的時候,那么服務(wù)器此時也不能確定請求里面的cookie是否具備完整性(假設(shè)請求是從一個低版本的主機(jī)發(fā)出的,即不支持Cookie-Integrity字段)。
同胞域。假設(shè)有這樣一種情況,example.com分別包含有一個可信的和一個不可信的子域,www.example.com?和?users.example.com。在對example.com設(shè)置cookie的時候,不可信的子域就可以注入可信子域的cookie字段。Cookie-Integrity字段并不能防止這種攻擊,但是我們可以通過增加一個字段來標(biāo)識每個cookie的來源(當(dāng)然這要取決于對帶寬和復(fù)雜性的考慮)。
我們在Firefox里用202行JavaScript代碼添加實現(xiàn)了Cookie-Integrity字段,并增加了一個Integrity?flag存儲到cookie里面,主要用來記錄這個cookie是否被設(shè)置成使用HTTPS傳輸。
七?總結(jié)和建議
CSRF是當(dāng)今一個被利用的非常廣泛的漏洞。很多網(wǎng)站修復(fù)了他們的包括登陸CSRF漏洞在內(nèi)的CSRF漏洞。基于這篇文章中提到的實驗和分析,我們建議網(wǎng)站在不同的情況下使用不同的CSRF防御策略。
??登陸CSRF。我們建議使用嚴(yán)格的Referer驗證策略來防御登陸CSRF,因為登陸的表單一般都是通過HTTPS發(fā)送,在合法請求里面的Referer都是真實可靠的。如果碰到?jīng)]有Referer字段的登陸請求,那么網(wǎng)站應(yīng)該直接拒絕以防御這種惡意的修改。
??HTTPS。對于那些專門使用HTTPS協(xié)議的網(wǎng)站,比如銀行類,我們也建議使用嚴(yán)格的Referer驗證策略來防御CSRF攻擊。對于那些有特定跨站需求的請求,網(wǎng)站應(yīng)該建立一份白名單,比如主頁等。
??第三方內(nèi)容。如果網(wǎng)站納入了第三方的內(nèi)容,比如圖像外鏈和超鏈接,網(wǎng)站應(yīng)該使用一個正確的驗證token?的框架,比如?Ruby-on-Rails。如果這樣的一個框架效果不好的話,網(wǎng)站就應(yīng)該花時間來設(shè)計更好的token?驗證策略,可以用HMAC方法將用戶的session與token?綁定到一起。
對于更長遠(yuǎn)的建議,我們希望能用Origin字段來替代Referer,因為這樣既保留了既有效果,又尊重了用戶的隱私。最終要廢除利用token來防御CSRF的方式,因為這樣網(wǎng)站就可以更好的保護(hù)無論是HTTP還是HTTPS請求,而不用擔(dān)心token是否會泄露。
未來的工作。如果使用Origin字段的方法來防御CSRF攻擊,網(wǎng)站要注意在處理GET請求的時候不要有什么副作用。盡管HTTP規(guī)范里已經(jīng)這樣要求,但是很多網(wǎng)站并沒有很好的遵守這一要求。讓網(wǎng)站都執(zhí)行這一要求正是我們未來的工作重點。
CSRF攻擊還興起了一個變種,即攻擊者在一個可信的網(wǎng)站嵌入一個frame并引誘用戶點擊(點擊劫持)。盡管從我們的定義上講,這個并不能算是CSRF攻擊,但是他們有一個很相似的地方就在于,攻擊者都是利用用戶的瀏覽器來對他信任的網(wǎng)站發(fā)起一個請求。防御這種攻擊的傳統(tǒng)辦法都是frame?busting,但是這種方法有個問題就是它依賴JavaScript,而JavaScript很有可能會被用戶或者攻擊者禁用。在這里我們有個建議是,可以在Origin字段里添加一些內(nèi)容用來描述frame的來源,也就是frame里面的超鏈接,這樣受信任的網(wǎng)站就可以根據(jù)frame的來源來決定是拒絕還是接受這個請求。