您的位置:首页 > 编程语言 > PHP开发

laravel的csrf token 的了解及使用

2017-10-24 15:57 411 查看
之前在项目中因为没有弄清楚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方法为:   

public function handle($request, Closure $next)
{
// 使用CSRF
//return parent::handle($request, $next);
// 禁用CSRF
return $next($request);
}


csrf的使用:

(1)在html的代码中加入:

<input type="hidden" name="_token" value="{{ csrf_token() }}" />


(2)使用cookie 方式 ,将app\Http\Middleware\VerifyCsrfToken.php修改为:

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier {

/**
* Handle an incoming request.
*
* @param  \Illuminate\Http\Request  $request
* @param  \Closure  $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return parent::addCookieToResponse($request, $next($request));
}

}


使用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中修改内容为:


protected function tokensMatch($request)
{
// If request is an ajax request, then check to see if token matches token provider in
// the header. This way, we can use CSRF protection in ajax requests also.
$token = $request->ajax() ? $request->header('X-CSRF-TOKEN') : $request->input('_token');
return $request->session()->token() == $token;
}

public function handle($request,\Closure $next){
//todo:需要在添加了登录验证之后,取消
   //这样是在post请求的时候不进行csrf token验证
if($request->method() == 'POST')
{
return $next($request);
}

return parent::handle($request,$next);
}


然后在vue中的bootstrap.js中的引入的axios的位置添加

window.axios.defaults.headers.common = {  'X-CSRF-TOKEN': document.querySelector('meta[name="X-CSRF-TOKEN"]').content,  'X-Requested-With': 'XMLHttpRequest'  };

在index.blade.php中添加

<meta name="X-CSRF-TOKEN" content="{{csrf_token()}}">


上面的代码都好理解,就是获取到 csrf_token令牌,然后提交,再经过中间件验证即可

下面重点来说一下 VerifyCsrfToken.php中间件

中间件的内容最开始应该只有一个 handle函数:这个是所有的都进行csrf token验证

public function handle($request,\Closure $next){
return parent::handle($request,$next);
}


现在项目中的这个中间件的内容

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
];
//    protected $except = [
//
//        '/classroom_upload',
//        'wk_upload',
//        'wechat',
//    ];
protected function tokensMatch($request)
{
// If request is an ajax request, then check to see if token matches token provider in
// the header. This way, we can use CSRF protection in ajax requests also.
$token = $request->ajax() ? $request->header('X-CSRF-TOKEN') : $request->input('_token');
return $request->session()->token() == $token;
}

public function handle($request,\Closure $next){
//todo:需要在添加了登录验证之后,取消
if($request->method() == 'POST')
{
return $next($request);
}

return parent::handle($request,$next);
}
}


我们来看一下 VerifyCsrfToken.php的源码     Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php;

<?php

namespace Illuminate\Foundation\Http\Middleware;

use Closure;
use Carbon\Carbon;
use Illuminate\Foundation\Application;
use Symfony\Component\HttpFoundation\Cookie;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Session\TokenMismatchException;

class VerifyCsrfToken
{
/**
* The application instance.
*
* @var \Illuminate\Foundation\Application
*/
protected $app;

/**
* The encrypter implementation.
*
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;

/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [];

/**
* Create a new middleware instance.
*
* @param  \Illuminate\Foundation\Application  $app
* @param  \Illuminate\Contracts\Encryption\Encrypter  $encrypter
* @return void
*/
public function __construct(Application $app, Encrypter $encrypter)
{
$this->app = $app;
$this->encrypter = $encrypter;
}

/**
* Handle an incoming request.
*
* @param  \Illuminate\Http\Request  $request
* @param  \Closure  $next
* @return mixed
*
* @throws \Illuminate\Session\TokenMismatchException
*/
public function handle($request, Closure $next)
{
if (
$this->isReading($request) ||
$this->runningUnitTests() ||
$this->inExceptArray($request) ||
$this->tokensMatch($request)
) {
return $this->addCookieToResponse($request, $next($request));
}

throw new TokenMismatchException;
}

/**
* Determine if the HTTP request uses a ‘read’ verb.
*
* @param  \Illuminate\Http\Request  $request
* @return bool
*/
protected function isReading($request)
{
return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}

/**
* Determine if the application is running unit tests.
*
* @return bool
*/
protected function runningUnitTests()
{
return $this->app->runningInConsole() && $this->app->runningUnitTests();
}

/**
* Determine if the request has a URI that should pass through CSRF verification.
*
* @param  \Illuminate\Http\Request  $request
* @return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}

if ($request->is($except)) {
return true;
}
}

return false;
}

/**
* Determine if the session and input CSRF tokens match.
*
* @param  \Illuminate\Http\Request  $request
* @return bool
*/
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request);

return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token);
}

/**
* Get the CSRF token from the request.
*
* @param  \Illuminate\Http\Request  $request
* @return string
*/
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;
}

/**
* Add the CSRF token to the response cookies.
*
* @param  \Illuminate\Http\Request  $request
* @param  \Symfony\Component\HttpFoundation\Response  $response
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function addCookieToResponse($request, $response)
{
$config = config('session');

$response->headers->setCookie(
new Cookie(
'XSRF-TOKEN', $request->session()->token(), Carbon::now()->getTimestamp() + 60 * $config['lifetime'],
$config['path'], $config['domain'], $config['secure'], false
)
);

return $response;
}
}


其中app下面的VerifyCsrfToken中间件是继承源码中的那个VerifyCsrfToken类

我们项目中重写了tokensMatch方法,然后调父类的handle的时候,父类中使用的是this调用tokensMatch的,个人感觉应该最后有用的是我们重写的这个方法,如果是ajax请求的话,我们就检测$request->header('X-CSRF-TOKEN')与session中的token是否一样 否则的话,就检测 $request->input('_token')与session中的token是否一样。

本人对laravel的原理还不太了解,上面的内容如果有什么错误的话,欢迎指教。

如需转载请注明:

本文出处:http://www.cnblogs.com/zhuchenglin/p/7723997.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: