在說(shuō)laravel框架里對(duì)CSRF的攻擊防護(hù)之前先對(duì)XSS和CSRF攻擊做一下簡(jiǎn)單的介紹灭红。
XSS和CSRF攻擊
XSS全稱(chēng)“跨站腳本”攻擊(Cross-site scripting)槽片。是通過(guò)利用網(wǎng)站的bug在頁(yè)面里注入惡意的js代碼挠轴,當(dāng)用戶打開(kāi)這些頁(yè)面的時(shí)候,這些惡意代碼就會(huì)在用戶毫無(wú)感知的情況下執(zhí)行嗽桩,從而實(shí)現(xiàn)一些用戶未經(jīng)授權(quán)的操作纫版,例如轉(zhuǎn)賬匯款胳挎,更改密碼等。
實(shí)施攻擊的比較常見(jiàn)的方式是通過(guò)發(fā)帖或評(píng)論等方式上傳數(shù)據(jù)到服務(wù)器迅细,比如發(fā)布一條包含這些數(shù)據(jù)的評(píng)論:<script type="text/javascript">alert('xss')</script>
巫橄,如果服務(wù)器沒(méi)有過(guò)濾或轉(zhuǎn)義上傳的這些評(píng)論數(shù)據(jù),直接就存入數(shù)據(jù)庫(kù)茵典。那么當(dāng)這個(gè)頁(yè)面再次被請(qǐng)求的時(shí)候湘换,這條評(píng)論會(huì)從數(shù)據(jù)庫(kù)取出,展示在前端頁(yè)面上,html會(huì)將<script>標(biāo)簽中的腳本解析執(zhí)行彩倚,完成xss攻擊筹我。
目前主流的瀏覽器都具有一定的xss防護(hù)機(jī)制澜建,這種類(lèi)型的xss可能在瀏覽器會(huì)直接過(guò)濾掉屈糊。這里主要說(shuō)明攻擊原理。
想象一下如果腳本的內(nèi)容不是alert('xss')
奕枝,而是調(diào)用發(fā)起轉(zhuǎn)賬接口或是將用戶的cookies上傳到攻擊者的服務(wù)器上(前提是cookies沒(méi)有設(shè)置http-only)哥谷,那用戶將會(huì)在毫不知情的情況下造成損失岸夯。
CSRF叫做跨站請(qǐng)求偽造(Cross-site request forgery),本質(zhì)上來(lái)說(shuō)XSS是實(shí)現(xiàn)CSRF攻擊的一種手段呼巷,它們都屬于跨站攻擊囱修。
laravel中CSRF的防護(hù)
在laravel中,框架是通過(guò)發(fā)送令牌的方式來(lái)實(shí)現(xiàn)CSRF防護(hù)的王悍。具體的實(shí)現(xiàn)過(guò)程是破镰,當(dāng)瀏覽器發(fā)起頁(yè)面請(qǐng)求的時(shí)候,laravel會(huì)返回給瀏覽器一個(gè)額外的cookie压储,cookie名為XSRF-TOKEN
鲜漩,值是一個(gè)隨機(jī)的字符串,且每一次請(qǐng)求都會(huì)變化集惋。
當(dāng)用戶在瀏覽器中通過(guò)點(diǎn)擊按鈕或其他方式發(fā)起POST請(qǐng)求時(shí)孕似,瀏覽器會(huì)自動(dòng)將cookies發(fā)送給后端,laravel通過(guò)反序列化session_id刮刑,拿到用戶session數(shù)據(jù)喉祭,然后對(duì)比用戶session中的_token
和XSRF-TOKEN
cookie的值是否匹配,如果不匹配雷绢,會(huì)返回419
返回碼泛烙。
注意,laravel對(duì)于
GET
請(qǐng)求是不做CSRF驗(yàn)證的翘紊。
通過(guò)這種機(jī)制蔽氨,就能夠防止非瀏覽器環(huán)境對(duì)非GET接口的直接調(diào)用。
防護(hù)的局限性
想象一下XSS攻擊的實(shí)現(xiàn)場(chǎng)景帆疟。如果頁(yè)面已經(jīng)被注入了js鹉究,那么通過(guò)js獲取XSRF-TOKEN
cookie不就是輕而易舉的事情了嗎?這樣子不照樣可以發(fā)起惡意請(qǐng)求了踪宠?
確實(shí)會(huì)存在這種風(fēng)險(xiǎn)自赔,所以在github上就有人提議將XSRF-TOKEN
這個(gè)cookie設(shè)置為http-only,以防止這種情況發(fā)生殴蓬。
cookie的http-only屬性設(shè)置為true后匿级,可以阻止JavaScript的訪問(wèn)
不過(guò)這個(gè)請(qǐng)求最終沒(méi)有被實(shí)施蟋滴。剛開(kāi)始我也不太理解,后來(lái)想想也確實(shí)沒(méi)有必要痘绎,而且也不應(yīng)該這樣做津函,因?yàn)檫@個(gè)令牌就是用于給js讀取的,然后js發(fā)送ajax請(qǐng)求之前讀取出此值孤页,然后添加上X-CSRF-TOKEN
頭部尔苦。laravel的驗(yàn)證機(jī)制就是基于此頭部的,而不是從cookie中獲取的值行施。如下是源碼:
/**
* Get the CSRF token from the request.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header, static::serialized());
}
return $token;
}
并且存放用戶session_id
的這個(gè)cookie是http-only的允坚,雖然惡意的js腳本可以獲取到XSRF-TOKEN
令牌,但是它獲取不到用戶的session_id
蛾号,這樣子即使有令牌在手稠项,你也無(wú)法假扮用戶執(zhí)行非授權(quán)操作。
http-only是框架的默認(rèn)配置鲜结,你也可以更改
session.php
配置文件展运,將其改為false,不過(guò)為了網(wǎng)站的安全性精刷,強(qiáng)烈不建議更改它拗胜。
關(guān)于此點(diǎn)的更詳細(xì)解釋?zhuān)梢圆榭次业?a href="http://www.reibang.com/p/2cfb676c58b1" target="_blank">這篇博文
新的攻擊方式
現(xiàn)在看下來(lái)好像只需要保證session_id
是http-only的,那么我們就可以高枕無(wú)憂怒允,無(wú)須擔(dān)心xss注入了埂软。既然惡意的js無(wú)法獲取到我的session_id
,無(wú)法冒充我本人纫事,那我還有什么好擔(dān)心的呢勘畔?
其實(shí)不然,我認(rèn)為還有兩點(diǎn)需要擔(dān)心:
其一丽惶,通過(guò)http協(xié)議發(fā)送出去的數(shù)據(jù)包有可能被中間人攻擊咖杂,被截獲,然后攻擊者從截獲的數(shù)據(jù)包中解析出用戶的cookies蚊夫,接下來(lái)再冒充用戶偽造請(qǐng)求發(fā)起攻擊。
此時(shí)懦尝,可以將站點(diǎn)升級(jí)成https知纷,然后將存有session_id
的這個(gè)cookie設(shè)置為secure
,即session.php
配置文件中的secure
配置項(xiàng)陵霉。
cookie的secure屬性表示只有當(dāng)協(xié)議是https時(shí)琅轧,瀏覽器才會(huì)傳輸此cookie給后端,否則就不發(fā)送踊挠。
其二乍桂,這里假設(shè)攻擊者將受害站點(diǎn)B
嵌入一個(gè)iframe中冲杀,然后你通過(guò)攻擊者的誘騙或其他不知情的方式訪問(wèn)到了攻擊者站點(diǎn)A
,并發(fā)起了http請(qǐng)求(點(diǎn)擊了按鈕或其他什么形式觸發(fā)的)睹酌,表面上看此請(qǐng)求是在A
站點(diǎn)發(fā)出的权谁,實(shí)際上可能你點(diǎn)擊的那個(gè)按鈕是B
站點(diǎn)的一個(gè)轉(zhuǎn)賬按鈕,攻擊者通過(guò)iframe
嵌入B
站點(diǎn)的形式憋沿,把那個(gè)按鈕包裝成了A
站點(diǎn)下看起來(lái)無(wú)害的一個(gè)按鈕旺芽,并誘使你點(diǎn)擊了它。如果你此時(shí)在站點(diǎn)B
是屬于登陸狀態(tài)的話辐啄,那么這次請(qǐng)求瀏覽器或自動(dòng)將你的cookies發(fā)送給后端采章,達(dá)到了攻擊目的。
對(duì)此的防護(hù)手段壶辜,就是防止頁(yè)面被iframe悯舟。有如下幾種方式來(lái)防止頁(yè)面被未經(jīng)授權(quán)的iframe:
- 使用js代碼來(lái)判斷:
if(window != window.top){
window.top.location.href = myOrigin;
}
注意:這個(gè)只判斷top是不太準(zhǔn)確的,可能循環(huán)嵌套砸民。比如:your-web -> attacker-web -> your-web抵怎。這種就判斷失敗了
后端返回一個(gè)
X-Frame-Options: deny
頭部,此方案可以在運(yùn)維端配置阱洪,通過(guò)配置nginx來(lái)實(shí)現(xiàn)便贵。X-Frame-Options
頭部的值還可以設(shè)置為其他的,具體參考文末的參考資料冗荸。通過(guò)CSP(Content Security Policy)機(jī)制來(lái)實(shí)現(xiàn)承璃。實(shí)現(xiàn)CSP機(jī)制也有兩種方案:a. 通過(guò)
Content Security Policy
頭部來(lái)控制。b. 通過(guò)在html代碼中加上meta
標(biāo)簽的形式來(lái)控制蚌本,例如<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
盔粹。
總結(jié)
綜上所述,為了防范CSRF攻擊程癌,我們要做好如下三點(diǎn):
- 對(duì)于用戶提交的數(shù)據(jù)做過(guò)濾和轉(zhuǎn)義舷嗡,防止
<script>
標(biāo)簽被存入。 - 對(duì)于cookie設(shè)置http-only嵌莉,阻止js訪問(wèn)之进萄。
- 防止頁(yè)面未經(jīng)授權(quán)的iframe。
當(dāng)然锐峭,各種攻擊手段和漏洞層出不窮中鼠,也許還有其他的攻擊形式我沒(méi)有想到,或許這三種手段也無(wú)法做到盡善盡美的防護(hù)沿癞,歡迎大家一起來(lái)留言討論研究援雇。
我相信只有從原理上理解了這些攻擊手段,才能從源頭找到應(yīng)對(duì)的策略椎扬,對(duì)于本文有表述的不到位的惫搏,或理解有誤的地方也歡迎大家提出指正具温。
完!
參考資料:
MDN - X-Frame-Options
MDN - Content Security Policy (CSP)
laravel - CSRF Protection
[Proposal] Allow setting XSRF-TOKEN cookie as httpOnly for XSS attacks
wikipedia - Cross-site request forgery
CSRF令牌為什么要通過(guò)HTTP頭部而不是cookie來(lái)驗(yàn)證