首先根據文件詳解csrf的流程機制,然後分析下怎麼在前後端分離的情況下進行csrf防護?
1.首先介紹token的生成,在 Illuminate\Session\Store.php 中start() 方法中,然後點選方法regenerateToken()方法中:
public function start()
{
$this->loadSession();
if (! $this->has('_token')) {
$this->regenerateToken();
}
return $this->started = true;
}
public function regenerateToken()
{
$this->put('_token', Str::random(40));
}
可見token是每次載入session時自動存入進session中。
2.然後看前臺獲取token值,首先表單中 {{csrf_token()}} 點入進去可以看到
function csrf_field()
{
return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">'); //點入進去csrf_token方法中
}
function csrf_token()
{
$session = app('session');
if (isset($session)) {
return $session->token();
}
throw new RuntimeException('Application session store not set.');
}
可以看到前臺token值是從session儲存的token值中獲取到的。
3.然後看中介軟體是怎麼驗證前臺提交的_token值:
在VerifyCsrfToken.php中繼承Middleware類,點選進入類中檢視handle方法
public function handle($request, Closure $next)
{
if (
$this->isReading($request) || //驗證請求方式,如果不在陣列['HEAD', 'GET', 'OPTIONS']中 返回false;
$this->runningUnitTests() || //檢視程式是否在進行測試 正式執行返回false;
$this->inExceptArray($request) || //檢視$except(白名單,不用驗證_token)陣列,如果請求路徑在陣列中返回true;
$this->tokensMatch($request) //進行token驗證
) {
return $this->addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
}
我們點選$this->tokensMatch()方法中可以看到請求中的token和session中的token是否相等:
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request); //從請求中獲取_token值
return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token); //session中的token和請求中的token是否相等;
}
點選進入$this->getTokenFromRequest() 方法中
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);
}
return $token;
}
從中可以看到獲取請求中token值的幾種方法:
from表單、頭部值X-CSRF-TOKEN、頭部值X-XSRF-TOKEN 這幾種方試,只要明白了中介軟體怎麼接收token值進行判斷,然後前臺就那麼幾種傳值方式了
第一種 form表單傳值 {{ csrf_field() }}或ajaxpost攜帶值 對應 $request->input('_token')
第二種 header頭傳值:
<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({ //修改jquery庫中程式碼
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
如果你用vue,只需將 resources/assets/js/bootstrap.js 引入你的檔案中 然後 meta標籤頭部值放入總模板裡,每次axios請求都會攜帶X-CSRF-TOKEN進入中介軟體進行驗證,下面為js檔案:
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://learnku.com/docs/laravel/csrf#csrf-x-csrf-token');
}
至此 從token值生成到前端攜帶token到後臺驗證已經完全走完一個流程了,接下來想一下假如前後端分離情況下怎麼進行token驗證
很簡單每次提交前用api的形式獲取token值傳入前端(csrf_token()獲取token值),然後前端用header頭形式或ajax形式傳入過來就可以了。
如果感覺有用就贊一下唄!!!