之前在项目中因为没有弄清楚csrf token的使用,导致发请求的话,一直请求失败,今天就一起来看一下csrf的一些东西。
1.Cross-site request forgery 跨站请求伪造,也被称为 “one click attack” 或者 session riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。CSRF 则通过伪装来自受信任用户的请求来利用受信任的网站。
2.从字面意思就可以理解:当你访问 fuck.com
黑客页面的时候,页面上放了一个按钮或者一个表单,URL/action 为 http://you.com/delete-myself
,这样引导或迫使甚至伪造用户触发按钮或表单。在浏览器发出 GET 或 POST 请求的时候,它会带上 you.com
的 cookie,如果网站没有做 CSRF 防御措施,那么这次请求在 you.com
看来会是完全合法的,这样就会对 you.com
的数据产生破坏。
3.第三方恶意网站也是可以构造post请求并提交至被攻击网站的,所以POST方式提交只是提高了攻击的门槛而已,无法防范CSRF攻击,所以对post也要进行防范
关于csrf更多的请参考 https://segmentfault.com/q/1010000000713614 https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/
在laravel中为了防止csrf 攻击,设计了 csrf token
laravel默认是开启了csrf token 验证的,关闭这个功能的方法:
(1)打开文件:app\Http\Kernel.php
把这行注释掉:‘App\Http\Middleware\VerifyCsrfToken’
(2)打开文件 app\Http\Middleware\VerifyCsrfToken.php
修改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的原理还不太了解,上面的内容如果有什么错误的话,欢迎指教。
如需转载请注明: