什么是CSRF攻擊
CSRF是跨站請求偽造(Cross-site request forgery)的英文縮寫。CSRF是跨站請求偽造(CSRF
)是一種通過偽裝授權(quán)用戶的請求來利用授信網(wǎng)站的惡意漏洞钻弄。
關(guān)于CSRF攻擊原理及其防護(hù),可查看Github上的這個(gè)項(xiàng)目:理解CSRF肘习,說得比較詳細(xì)和透徹泛源。
Laravel 中如何避免CSRF攻擊
Laravel 讓應(yīng)用避免遭到跨站請求偽造攻擊變得簡單。
Laravel框架中避免CSRF攻擊很簡單:Laravel自動(dòng)為每個(gè)用戶Session生成了一個(gè)CSRF Token薪者,該Token可用于驗(yàn)證登錄用戶和發(fā)起請求者是否是同一人竿裂,如果不是則請求失敗玉吁。
Laravel提供了一個(gè)全局幫助函數(shù)csrf_token
來獲取該Token值,因此只需在視提交圖表單中添加如下HTML代碼即可在請求中帶上Token:
<form method="POST" action="/profile">
{{ csrf_field() }}
...
</form>
中間件組 web
中的中間件 VerifyCsrfToken
會自動(dòng)為我們驗(yàn)證請求輸入的 token
值和 Session
中存儲的 token 是否一致腻异。
從 CSRF 保護(hù)中排除指定 URL
并不是所有請求都需要避免CSRF攻擊进副,比如去第三方API獲取數(shù)據(jù)的請求』诔#可以通過在 VerifyCsrfToken
中間件中將要排除的請求URL添加到 $except
屬性數(shù)組中:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier{
/**
* 指定從 CSRF 驗(yàn)證中排除的URL
*
* @var array
*/
protected $except = [
'testCsrf',
];
}
這樣我們刷新頁面影斑,再次在http://laravel.app:8000/testCsrf
頁面中點(diǎn)擊“Test”按鈕,則頁面不會報(bào)錯(cuò)机打,正常輸出如下內(nèi)容:
Success!
X-CSRF-Token 及其使用
如果使用 Ajax 提交POST表單矫户,又該如何處理呢?我們可以將Token設(shè)置在meta中:
<meta name="csrf-token" content="{{ csrf_token() }}">
然后在全局Ajax中使用這種方式設(shè)置X-CSRF-Token請求頭并提交:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Laravel 的VerifyCsrfToken
中間件會檢查X-CSRF-TOKEN
請求頭残邀,如果該值和Session
中CSRF值
相等則驗(yàn)證通過皆辽,否則不通過。
X-XSRF-Token 及其使用
除此之外芥挣,Laravel還會將CSRF
的值保存到名為XSRF-TOKEN
的 Cookie
中膳汪,然后在VerifyCsrfToken
中間件驗(yàn)證該值,當(dāng)然九秀,我們不需要手動(dòng)做任何操作,一些JavaScript框架如Angular會自動(dòng)幫我們實(shí)現(xiàn)粘我。
Laravel中CSRF驗(yàn)證原理分析
首先
首先Laravel開啟Session時(shí)會生成一個(gè)token值并存放在Session中(Illuminate\Session\Store.php
第90行start
方法)鼓蜒,對應(yīng)源碼如下:
public function start()
{
$this->loadSession();
if (! $this->has('_token')) {
$this->regenerateToken();
}
return $this->started = true;
}
然后
然后重點(diǎn)分析VerifyToken
中間件的handle
方法痹换,該方法中先通過 isReading
方法判斷請求方式,如果請求方法是 HEAD
都弹、GET
娇豫、OPTIONS
其中一種,則不做CSRF驗(yàn)證畅厢;
再然后
再通過shouldPassThrough
方法判斷請求路由是否在$excpet
屬性數(shù)組中進(jìn)行了排除冯痢,如果做了排除也不做驗(yàn)證;
最后
最后通過 tokensMatch
方法判斷請求參數(shù)中的 CSRF TOKEN
值和Session
中的Token
值是否相等框杜,如果相等則通過驗(yàn)證浦楣,否則拋出TokenMismatchException
異常。對應(yīng)源碼如下:
public function handle($request, Closure $next)
{
if ($this->isReading($request) || $this->shouldPassThrough($request) || $this->tokensMatch($request)) {
return $this->addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
}
注:
tokensMatch
方法首先從 Request 中獲取_token
參數(shù)值咪辱,如果請求中不包含該參數(shù)則獲取X-CSRF-TOKEN
請求頭的值振劳,如果該請求頭也不存在則獲取X-XSRF-TOKEN
請求頭的值,需要注意的是X-XSRF-TOKEN
請求頭的值需要調(diào)用Encrypter 的decrypt
方法進(jìn)行解密油狂。