CSRF(Cross-site request forgery丰歌,中文為跨站請求偽造)是一種利用網(wǎng)站可信用戶的權(quán)限去執(zhí)行未授權(quán)的命令的一種惡意攻擊。通過偽裝可信用戶的請求來利用信任該用戶的網(wǎng)站堕澄,這種攻擊方式雖然不是很流行聚请,但是卻難以防范,其危害也不比其他安全漏洞小拣帽。
本文將簡要介紹CSRF產(chǎn)生的原因以及利用方式涡拘,然后對如何避免這種攻擊方式提供一些可供參考的方案玲躯,希望廣大程序猿們都能夠?qū)@種攻擊方式有所了解,避免自己開發(fā)的應(yīng)用被別人利用鳄乏。
CSRF也稱作one-click attack或者session riding跷车,其簡寫有時候也會使用XSRF。
[TOC]
本文將會持續(xù)修正和更新橱野,最新內(nèi)容請參考我的 GITHUB 上的 程序猿成長計劃 項目姓赤,歡迎 Star,更多精彩內(nèi)容請 follow me仲吏。
什么是CSRF不铆?
簡單點說,CSRF攻擊就是 攻擊者利用受害者的身份裹唆,以受害者的名義發(fā)送惡意請求誓斥。與XSS(Cross-site scripting,跨站腳本攻擊)不同的是许帐,XSS的目的是獲取用戶的身份信息劳坑,攻擊者竊取到的是用戶的身份(session/cookie),而CSRF則是利用用戶當(dāng)前的身份去做一些未經(jīng)過授權(quán)的操作成畦。
CSRF攻擊最早在2001年被發(fā)現(xiàn)距芬,由于它的請求是從用戶的IP地址發(fā)起的,因此在服務(wù)器上的web日志中可能無法檢測到是否受到了CSRF攻擊循帐,正是由于它的這種隱蔽性框仔,很長時間以來都沒有被公開的報告出來,直到2007年才真正的被人們所重視拄养。
CSRF有哪些危害
CSRF可以盜用受害者的身份离斩,完成受害者在web瀏覽器有權(quán)限進行的任何操作,想想吧,能做的事情太多了跛梗。
- 以你的名義發(fā)送詐騙郵件寻馏,消息
- 用你的賬號購買商品
- 用你的名義完成虛擬貨幣轉(zhuǎn)賬
- 泄露個人隱私
- ...
產(chǎn)生原理以及利用方式
要完成一個CSRF攻擊,必須具備以下幾個條件:
- 受害者已經(jīng)登錄到了目標網(wǎng)站(你的網(wǎng)站)并且沒有退出
- 受害者有意或者無意的訪問了攻擊者發(fā)布的頁面或者鏈接地址
(圖片來自網(wǎng)絡(luò)核偿,出處不明诚欠,百度來的??)
整個步驟大致是這個樣子的:
- 用戶小明在你的網(wǎng)站A上面登錄了,A返回了一個session ID(使用cookie存儲)
- 小明的瀏覽器保持著在A網(wǎng)站的登錄狀態(tài)漾岳,事實上幾乎所有的網(wǎng)站都是這樣做的聂薪,一般至少是用戶關(guān)閉瀏覽器之前用戶的會話是不會結(jié)束的
- 攻擊者小強給小明發(fā)送了一個鏈接地址,小明打開了這個地址蝗羊,查看了網(wǎng)頁的內(nèi)容
- 小明在打開這個地址的時候,這個頁面已經(jīng)自動的對網(wǎng)站A發(fā)送了一個請求仁锯,這時候因為A網(wǎng)站沒有退出耀找,因此只要請求的地址是A的就會攜帶A的cookie信息,也就是使用A與小明之間的會話
- 這時候A網(wǎng)站肯定是不知道這個請求其實是小強偽造的網(wǎng)頁上發(fā)送的业崖,而是誤以為小明就是要這樣操作野芒,這樣小強就可以隨意的更改小明在A上的信息,以小明的身份在A網(wǎng)站上進行操作
利用方式
利用CSRF攻擊双炕,主要包含兩種方式狞悲,一種是基于GET請求方式的利用,另一種是基于POST請求方式的利用妇斤。
GET請求利用
使用GET請求方式的利用是最簡單的一種利用方式摇锋,其隱患的來源主要是由于在開發(fā)系統(tǒng)的時候沒有按照HTTP動詞的正確使用方式來使用造成的。對于GET請求來說站超,它所發(fā)起的請求應(yīng)該是只讀的荸恕,不允許對網(wǎng)站的任何內(nèi)容進行修改。
但是事實上并不是如此死相,很多網(wǎng)站在開發(fā)的時候融求,研發(fā)人員錯誤的認為GET/POST的使用區(qū)別僅僅是在于發(fā)送請求的數(shù)據(jù)是在Body中還是在請求地址中,以及請求內(nèi)容的大小不同算撮。對于一些危險的操作比如刪除文章生宛,用戶授權(quán)等允許使用GET方式發(fā)送請求,在請求參數(shù)中加上文章或者用戶的ID肮柜,這樣就造成了只要請求地址被調(diào)用陷舅,數(shù)據(jù)就會產(chǎn)生修改。
現(xiàn)在假設(shè)攻擊者(用戶ID=121)想將自己的身份添加為網(wǎng)站的管理員审洞,他在網(wǎng)站A上面發(fā)了一個帖子蔑赘,里面包含一張圖片,其地址為http://a.com/user/grant_super_user/121
![](http://a.com/user/grant_super_user/121)
設(shè)想管理員看到這個帖子的時候,這個圖片肯定會自動加載顯示的缩赛。于是在管理員不知情的情況下耙箍,一個賦予用戶管理員權(quán)限的操作已經(jīng)悄悄的以他的身份執(zhí)行了。這時候攻擊者121就獲取到了網(wǎng)站的管理員權(quán)限酥馍。
POST請求利用
相對于GET方式的利用辩昆,POST方式的利用更加復(fù)雜一些,難度也大了一些旨袒。攻擊者需要偽造一個能夠自動提交的表單來發(fā)送POST請求汁针。
<script>
$(function() {
$('#CSRF_forCSRFm').trigger('submit');
});
</script>
<form action="http://a.com/user/grant_super_user" id="CSRF_form" method="post">
<input name="uid" value="121" type="hidden">
</form>
只要想辦法實現(xiàn)用戶訪問的時候自動提交表單就可以了。
如何防范
防范原理
防范CSRF攻擊砚尽,其實本質(zhì)就是要求網(wǎng)站能夠識別出哪些請求是非正常用戶主動發(fā)起的施无。這就要求我們在請求中嵌入一些額外的授權(quán)數(shù)據(jù),讓網(wǎng)站服務(wù)器能夠區(qū)分出這些未授權(quán)的請求必孤,比如說在請求參數(shù)中添加一個字段猾骡,這個字段的值從登錄用戶的Cookie或者頁面中獲取的(這個字段的值必須對每個用戶來說是隨機的,不能有規(guī)律可循)敷搪。攻擊者偽造請求的時候是無法獲取頁面中與登錄用戶有關(guān)的一個隨機值或者用戶當(dāng)前cookie中的內(nèi)容的兴想,因此就可以避免這種攻擊。
防范技術(shù)
Synchronizer token pattern
令牌同步模式(Synchronizer token pattern赡勘,簡稱STP)是在用戶請求的頁面中的所有表單中嵌入一個token嫂便,在服務(wù)端驗證這個token的技術(shù)。token可以是任意的內(nèi)容闸与,但是一定要保證無法被攻擊者猜測到或者查詢到毙替。攻擊者在請求中無法使用正確的token,因此可以判斷出未授權(quán)的請求践樱。
Cookie-to-Header Token
對于使用Js作為主要交互技術(shù)的網(wǎng)站蔚龙,將CSRF的token寫入到cookie中
Set-Cookie: CSRF-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
然后使用javascript讀取token的值,在發(fā)送http請求的時候?qū)⑵渥鳛檎埱蟮膆eader
X-CSRF-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
最后服務(wù)器驗證請求頭中的token是否合法映胁。
驗證碼
使用驗證碼可以杜絕CSRF攻擊木羹,但是這種方式要求每個請求都輸入一個驗證碼,顯然沒有哪個網(wǎng)站愿意使用這種粗暴的方式解孙,用戶體驗太差坑填,用戶會瘋掉的。
簡單實現(xiàn)STP
首先在index.php中弛姜,創(chuàng)建一個表單脐瑰,在表單中,我們將session中存儲的token放入到隱藏域廷臼,這樣苍在,表單提交的時候token會隨表單一起提交
<?php
$token = sha1(uniqid(rand(), true));
$_SESSION['token'] = $token;
?>
<form action="buy.php" method="post">
<input type="hidden" name="token" value="<?=$token; ?>" />
... 表單內(nèi)容
</form>
在服務(wù)端校驗請求參數(shù)的buy.php
中绝页,對表單提交過來的token與session中存儲的token進行比對,如果一致說明token是有效的
<?php
if ($_POST['token'] != $_SESSION['token']) {
// TOKEN無效
throw new \Exception('Token無效寂恬,請求為偽造請求');
}
// TOKEN有效续誉,表單內(nèi)容處理
對于攻擊者來說,在偽造請求的時候是無法獲取到用戶頁面中的這個token
值的初肉,因此就可以識別出其創(chuàng)建的偽造請求酷鸦。
解析Laravel框架中的VerifyCSRFToken中間件
在Laravel框架中,使用了VerifyCSRFToken
這個中間件來防范CSRF攻擊牙咏。
在頁面的表單中使用{{ CSRF_field() }}
來生成token臼隔,該函數(shù)會在表單中添加一個名為_token
的隱藏域,該隱藏域的值為Laravel生成的token妄壶,Laravel使用隨機生成的40個字符作為防范CSRF攻擊的token摔握。
$this->put('_token', Str::random(40));
如果請求是ajax異步請求,可以在meta
標簽中添加token
<meta name="CSRF-token" content="{{ CSRF_token() }}">
使用jquery
作為前端的框架時候丁寄,可以通過以下配置將該值添加到所有的異步請求頭中
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="CSRF-token"]').attr('content')
}
});
在啟用session的時候氨淌,Laravel會生成一個名為_token
的值存儲到session中。而使用前面兩種方式在頁面中加入的token就是使用的這一個值狡逢。在用戶請求到來時,VerifyCSRFToken
中間件會對符合條件的請求進行CSRF檢查
if (
$this->isReading($request) ||
$this->runningUnitTests() ||
$this->shouldPassThrough($request) ||
$this->tokensMatch($request)
) {
return $this->addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
在if
語句中有四個條件拼卵,只要任何一個條件結(jié)果為true
則任何該請求是合法的奢浑,否則就會拋出TokenMismatchException
異常,告訴用戶請求不合法腋腮,存在CSRF攻擊雀彼。
第一個條件$this->isReading($request)
用來檢查請求是否會對數(shù)據(jù)產(chǎn)生修改
protected function isReading($request)
{
return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}
這里判斷了請求方式,如果是HEAD
即寡,GET
徊哑,OPTIONS
這三種請求方式則直接放行。你可能會感到疑惑聪富,為什么GET請求也要放行呢莺丑?這是因為Laravel認為這三個請求都是請求查詢數(shù)據(jù)的,如果一個請求是使用GET方式墩蔓,那無論請求多少次梢莽,無論請求參數(shù)如何,都不應(yīng)該最數(shù)據(jù)做任何修改奸披。
第二個條件顧名思義是對單元測試進行放行昏名,第三個是為開發(fā)者提供了一個可以對某些請求添加例外的功能,最后一個$this->tokensMatch($request)
則是真正起作用的一個阵面,它是Laravel防范CSRF攻擊的關(guān)鍵
$sessionToken = $request->session()->token();
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
if (! is_string($sessionToken) || ! is_string($token)) {
return false;
}
return hash_equals($sessionToken, $token);
Laravel會從請求中讀取_token
參數(shù)的的值轻局,這個值就是在前面表單中添加的CSRF_field()
函數(shù)生成的洪鸭。如果請求是異步的,那么會讀取X-CSRF-TOKEN
請求頭仑扑,從請求頭中讀取token的值览爵。
最后使用hash_equals
函數(shù)驗證請求參數(shù)中提供的token值和session中存儲的token值是否一致,如果一致則說明請求是合法的夫壁。
你可能注意到拾枣,這個檢查過程中也會讀取一個名為X-XSRF-TOKEN
的請求頭,這個值是為了提供對一些javascript框架的支持(比如Angular)盒让,它們會自動的對異步請求中添加該請求頭梅肤,而該值是從Cookie中的XSRF-TOKEN
中讀取的,因此在每個請求結(jié)束的時候邑茄,Laravel會發(fā)送給客戶端一個名為XSRF-TOKEN
的Cookie值
$response->headers->setCookie(
new Cookie(
'XSRF-TOKEN', $request->session()->token(), time() + 60 * $config['lifetime'],
$config['path'], $config['domain'], $config['secure'], false
)
);
寫在最后
本文只是對CSRF做了一個簡單的介紹姨蝴,主要是側(cè)重于CSRF是什么以及如何應(yīng)對CSRF攻擊。有一個事實是我們無法回避的:沒有絕對安全的系統(tǒng)肺缕,你有一千種防御對策左医,攻擊者就有一千零一種攻擊方式,但不管如何同木,我們都要盡最大的努力去將攻擊者攔截在門外浮梢。如果希望深入了解如何發(fā)起一個CSRF攻擊,可以參考一下這篇文章 從零開始學(xué)CSRF彤路。
作為一名web方向的研發(fā)人員秕硝,無論你是從事業(yè)務(wù)邏輯開發(fā)還是做單純的技術(shù)研究,了解一些安全方面的知識都是很有必要的洲尊,多關(guān)注一些安全方向的動態(tài)远豺,了解常見的攻擊方式以及應(yīng)對策略,必將在你成長為一名大牛的路上為你“推波助瀾”坞嘀。
本文將會持續(xù)修正和更新躯护,最新內(nèi)容請參考我的 GITHUB 上的 程序猿成長計劃 項目,歡迎 Star丽涩,更多精彩內(nèi)容請 follow me棺滞。