以前在项目中由于没有弄清楚csrf token的使用,致使发请求的话,一直请求失败,今天就一块儿来看一下csrf的一些东西。php
1.Cross-site request forgery 跨站请求伪造,也被称为 “one click attack” 或者 session riding,一般缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。CSRF 则经过假装来自受信任用户的请求来利用受信任的网站。html
2.从字面意思就能够理解:当你访问 fuck.com
黑客页面的时候,页面上放了一个按钮或者一个表单,URL/action 为 http://you.com/delete-myself
,这样引导或迫使甚至伪造用户触发按钮或表单。在浏览器发出 GET 或 POST 请求的时候,它会带上 you.com
的 cookie,若是网站没有作 CSRF 防护措施,那么此次请求在 you.com
看来会是彻底合法的,这样就会对 you.com
的数据产生破坏。vue
3.第三方恶意网站也是能够构造post请求并提交至被攻击网站的,因此POST方式提交只是提升了攻击的门槛而已,没法防范CSRF攻击,因此对post也要进行防范ios
关于csrf更多的请参考 https://segmentfault.com/q/1010000000713614 https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/laravel
在laravel中为了防止csrf 攻击,设计了 csrf tokenweb
laravel默认是开启了csrf token 验证的,关闭这个功能的方法:ajax
(1)打开文件:app\Http\Kernel.phpbootstrap
把这行注释掉:‘App\Http\Middleware\VerifyCsrfToken’axios
(2)打开文件 app\Http\Middleware\VerifyCsrfToken.phpsegmentfault
修改handle方法为:
1 public function handle($request, Closure $next) 2 { 3 // 使用CSRF 4 //return parent::handle($request, $next); 5 // 禁用CSRF 6 return $next($request); 7 }
csrf的使用:
(1)在html的代码中加入:
1 <input type="hidden" name="_token" value="{{ csrf_token() }}" />
(2)使用cookie 方式 ,将app\Http\Middleware\VerifyCsrfToken.php修改成:
1 <?php namespace App\Http\Middleware; 2 3 use Closure; 4 use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier; 5 6 class VerifyCsrfToken extends BaseVerifier { 7 8 /** 9 * Handle an incoming request. 10 * 11 * @param \Illuminate\Http\Request $request 12 * @param \Closure $next 13 * @return mixed 14 */ 15 public function handle($request, Closure $next) 16 { 17 return parent::addCookieToResponse($request, $next($request)); 18 } 19 20 }
使用cookie方法就不用在每一个页面都加入这个input 的 hidden 标签
还能够部分使用csrf检测部分不使用。
注:本文从laravel的csrf token开始到此参考:http://blog.csdn.net/proud2005/article/details/49995389
关于 laravel 的 csrf 保护更多的内容请参考 laravel学院文档:http://laravelacademy.org/post/6742.html
下面说说咱们那个项目中的关于csrf token的使用:
在个人另外一篇文章中也提到了咱们那个项目中的使用过程
在中间件VerifyCsrfToken.php中修改内容为:
1 protected function tokensMatch($request) 2 { 3 // If request is an ajax request, then check to see if token matches token provider in 4 // the header. This way, we can use CSRF protection in ajax requests also. 5 $token = $request->ajax() ? $request->header('X-CSRF-TOKEN') : $request->input('_token'); 6 return $request->session()->token() == $token; 7 } 8 9 public function handle($request,\Closure $next){ 10 //todo:须要在添加了登陆验证以后,取消 11 //这样是在post请求的时候不进行csrf token验证 12 if($request->method() == 'POST') 13 { 14 return $next($request); 15 } 16 17 return parent::handle($request,$next); 18 }
而后在vue中的bootstrap.js中的引入的axios的位置添加
1 window.axios.defaults.headers.common = { 2 'X-CSRF-TOKEN': document.querySelector('meta[name="X-CSRF-TOKEN"]').content, 3 'X-Requested-With': 'XMLHttpRequest' 4 };
在index.blade.php中添加
1 <meta name="X-CSRF-TOKEN" content="{{csrf_token()}}">
上面的代码都好理解,就是获取到 csrf_token令牌,而后提交,再通过中间件验证便可
下面重点来讲一下 VerifyCsrfToken.php中间件
中间件的内容最开始应该只有一个 handle函数:这个是全部的都进行csrf token验证
1 public function handle($request,\Closure $next){ 2 return parent::handle($request,$next); 3 }
如今项目中的这个中间件的内容
1 <?php 2 3 namespace App\Http\Middleware; 4 5 use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier; 6 7 class VerifyCsrfToken extends BaseVerifier 8 { 9 /** 10 * The URIs that should be excluded from CSRF verification. 11 * 12 * @var array 13 */ 14 protected $except = [ 15 // 16 ]; 17 // protected $except = [ 18 // 19 // '/classroom_upload', 20 // 'wk_upload', 21 // 'wechat', 22 // ]; 23 protected function tokensMatch($request) 24 { 25 // If request is an ajax request, then check to see if token matches token provider in 26 // the header. This way, we can use CSRF protection in ajax requests also. 27 $token = $request->ajax() ? $request->header('X-CSRF-TOKEN') : $request->input('_token'); 28 return $request->session()->token() == $token; 29 } 30 31 32 public function handle($request,\Closure $next){ 33 //todo:须要在添加了登陆验证以后,取消 34 if($request->method() == 'POST') 35 { 36 return $next($request); 37 } 38 39 return parent::handle($request,$next); 40 } 41 }
咱们来看一下 VerifyCsrfToken.php的源码 Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php;
1 <?php 2 3 namespace Illuminate\Foundation\Http\Middleware; 4 5 use Closure; 6 use Carbon\Carbon; 7 use Illuminate\Foundation\Application; 8 use Symfony\Component\HttpFoundation\Cookie; 9 use Illuminate\Contracts\Encryption\Encrypter; 10 use Illuminate\Session\TokenMismatchException; 11 12 class VerifyCsrfToken 13 { 14 /** 15 * The application instance. 16 * 17 * @var \Illuminate\Foundation\Application 18 */ 19 protected $app; 20 21 /** 22 * The encrypter implementation. 23 * 24 * @var \Illuminate\Contracts\Encryption\Encrypter 25 */ 26 protected $encrypter; 27 28 /** 29 * The URIs that should be excluded from CSRF verification. 30 * 31 * @var array 32 */ 33 protected $except = []; 34 35 /** 36 * Create a new middleware instance. 37 * 38 * @param \Illuminate\Foundation\Application $app 39 * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter 40 * @return void 41 */ 42 public function __construct(Application $app, Encrypter $encrypter) 43 { 44 $this->app = $app; 45 $this->encrypter = $encrypter; 46 } 47 48 /** 49 * Handle an incoming request. 50 * 51 * @param \Illuminate\Http\Request $request 52 * @param \Closure $next 53 * @return mixed 54 * 55 * @throws \Illuminate\Session\TokenMismatchException 56 */ 57 public function handle($request, Closure $next) 58 { 59 if ( 60 $this->isReading($request) || 61 $this->runningUnitTests() || 62 $this->inExceptArray($request) || 63 $this->tokensMatch($request) 64 ) { 65 return $this->addCookieToResponse($request, $next($request)); 66 } 67 68 throw new TokenMismatchException; 69 } 70 71 /** 72 * Determine if the HTTP request uses a ‘read’ verb. 73 * 74 * @param \Illuminate\Http\Request $request 75 * @return bool 76 */ 77 protected function isReading($request) 78 { 79 return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']); 80 } 81 82 /** 83 * Determine if the application is running unit tests. 84 * 85 * @return bool 86 */ 87 protected function runningUnitTests() 88 { 89 return $this->app->runningInConsole() && $this->app->runningUnitTests(); 90 } 91 92 /** 93 * Determine if the request has a URI that should pass through CSRF verification. 94 * 95 * @param \Illuminate\Http\Request $request 96 * @return bool 97 */ 98 protected function inExceptArray($request) 99 { 100 foreach ($this->except as $except) { 101 if ($except !== '/') { 102 $except = trim($except, '/'); 103 } 104 105 if ($request->is($except)) { 106 return true; 107 } 108 } 109 110 return false; 111 } 112 113 /** 114 * Determine if the session and input CSRF tokens match. 115 * 116 * @param \Illuminate\Http\Request $request 117 * @return bool 118 */ 119 protected function tokensMatch($request) 120 { 121 $token = $this->getTokenFromRequest($request); 122 123 return is_string($request->session()->token()) && 124 is_string($token) && 125 hash_equals($request->session()->token(), $token); 126 } 127 128 /** 129 * Get the CSRF token from the request. 130 * 131 * @param \Illuminate\Http\Request $request 132 * @return string 133 */ 134 protected function getTokenFromRequest($request) 135 { 136 $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN'); 137 138 if (! $token && $header = $request->header('X-XSRF-TOKEN')) { 139 $token = $this->encrypter->decrypt($header); 140 } 141 142 return $token; 143 } 144 145 /** 146 * Add the CSRF token to the response cookies. 147 * 148 * @param \Illuminate\Http\Request $request 149 * @param \Symfony\Component\HttpFoundation\Response $response 150 * @return \Symfony\Component\HttpFoundation\Response 151 */ 152 protected function addCookieToResponse($request, $response) 153 { 154 $config = config('session'); 155 156 $response->headers->setCookie( 157 new Cookie( 158 'XSRF-TOKEN', $request->session()->token(), Carbon::now()->getTimestamp() + 60 * $config['lifetime'], 159 $config['path'], $config['domain'], $config['secure'], false 160 ) 161 ); 162 163 return $response; 164 } 165 }
其中app下面的VerifyCsrfToken中间件是继承源码中的那个VerifyCsrfToken类
咱们项目中重写了tokensMatch方法,而后调父类的handle的时候,父类中使用的是this调用tokensMatch的,我的感受应该最后有用的是咱们重写的这个方法,若是是ajax请求的话,咱们就检测$request->header('X-CSRF-TOKEN')与session中的token是否同样 不然的话,就检测 $request->input('_token')与session中的token是否同样。
本人对laravel的原理还不太了解,上面的内容若是有什么错误的话,欢迎指教。
如需转载请注明: