這個錯誤是剛學習Laravel的時候碰到的,只是當時還沒開始寫部落格,一直也沒記錄下來,今天下午又碰到了這個問題,趁著這會兒沒啥事,趕緊總結下。 一、為什麼報這個錯誤
答:這是由於laravel架構內建的csrf_token防護中介軟體的原因。這個中介軟體的位置在/app/middleware/VrifyCsrfToken.php。這個中介軟體的作用就是為了過濾Post請求。
Laravel自動為每個使用者Session產生了一個CSRF Token,該Token可用於驗證登入使用者和發起要求者是否是同一人,如果不是則請求失敗。Laravel提供了一個全域協助函數csrf_token(本地存放在D:\www\laravel5.1\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php)來擷取該Token值。 二、什麼是csrf防護
CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站指令碼(XSS),但它與XSS非常不同,XSS利用網站內的信任使用者,而CSRF則通過偽裝來自受信任使用者的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防範的資源也相當稀少)和難以防範,所以被認為比XSS更具危險性。
從我們程式員的角度來看的話,請參考下面這個連結:
http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html 三、如果避免這個錯誤。
1、在post方式提交表單的時候,加上laravel內建的全域協助函數csrf_token。
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
這段代碼的意思是在提交表單的時候,會自動帶上laravel產生的csrf_token()的值,然後在訪問路由的時候,laravel會判斷這個值,失敗則報錯:TokenMismatchException,成功則正常訪問路由。
2、如果進行ajax的post請求的時候並沒有提交form,表單,此時我們可以通過在meta中寫入一些屬性來金星csrf防護。
<head> <title>Laravel</title> <meta name="csrf-token" content="{{ csrf_token() }}"></head>
在ajax中,我們需要加上header屬性:
$.ajax({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, url: "{{url('/rsa_post')}}", type: "post", dataType: "json",
這樣,我們就可以正確的訪問路由了。
以上兩種方法我都試了一下,絕對是沒問題的,可以有效避免錯誤。 四、當我們不想啟用架構內建的csrf防護的時候
進入:laravel/app/Middleware/VerifyCsrfToken.php
1、找到csrf的中介軟體,然後可以按照我下面的代碼來修改
public function handle($request, Closure $next) { // 使用CSRF return parent::handle($request, $next); // 禁用CSRF //return $next($request); }
當使用CSRF的時候,選用上面的代碼。當禁用的時候,選用下面的代碼。
2、有的時候我們既需要開啟CSRF防護,又需要在一些特性的post請求時不帶csrf_token(),怎麼辦。
答:laravel架構為我們提供了一個特殊的屬性,如下所示:
class VerifyCsrfToken extends BaseVerifier{ /** * The URIs that should be excluded from CSRF verification. * * @var array */ protected $except = [ // 'upload', 'rsa_post', ];/* public function handle($request, Closure $next) { // 使用CSRF return parent::handle($request, $next); // 禁用CSRF //return $next($request); }*/}
這段代碼的意思是利用 except來進行路由過濾。在我們 except來進行路由過濾。在我們except中的是我們不想被防護的路由名稱。此處的’upload’和’rsa_post’,都是我需要post方式訪問的路由。大家可以自己試一下,經過測試,這種方式是完全沒問題的。 五、laravel實現CSRF防護的原始碼解析。
1、原始碼位置:
由中介軟體中的代碼可知,laravel是通過中介軟體的handle()方法進行防護的。有IDE的同學可以直接追蹤下該函數。沒有用IDE編輯器的同學可以通過一定的路徑來訪問源碼。
路徑:laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCrsfToken.php
源碼如下:
public function handle($request, Closure $next) { if ( $this->isReading($request) || $this->runningUnitTests() || $this->shouldPassThrough($request) || $this->tokensMatch($request) ) { return $this->addCookieToResponse($request, $next($request)); } throw new TokenMismatchException; }
這裡大家可以根據源碼中的幾個方法來瞭解laravel的防護機制
2、源碼分析:
1)首先Laravel開啟Session時會產生一個token值並存放在Session中(Illuminate\Session\Store.php第90行start方法),對應源碼如下:
public function start(){ // 讀取session $this->loadSession(); if (! $this->has('_token')) { $this->regenerateToken(); } return $this->started = true;}
2)然後重點分析VerifyToken中介軟體的handle方法,該方法中先通過isReading方法判斷請求方式,如果要求方法是HEAD、GET、OPTIONS其中一種,則不做CSRF驗證;
3)再通過shouldPassThrough方法判斷請求路由是否在$excpet屬性數組中進行了排除,如果做了排除也不做驗證;
4)關於這個runningUnitTests()方法,我也不太明白這個是幹嘛的。後來通過百度,大致知道了這個函數是為了檢測laravel項目的運行環境的。如果是’cli’啟動的話,則正常。
5)最後通過tokensMatch方法判斷請求參數中的CSRF TOKEN值和Session中的Token值是否相等,如果相等則通過驗證,否則拋出TokenMismatchException異常。
對應源碼如下:
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;}
以上都是laravel關於CSRF防護的內容。果然不懂的時候看源碼才是王道啊。
end