Laravel之CSRF

excluding-csrf

    最近主要在弄Laravel的东西,所以就做一个有关CSRF的讨论,毕竟CSRF安全是一个很重要的话题,本次主要讨论下Laravel中提供的CSRF的几种用法。

由于Laravel自5之后的版本改动较为频繁,现特此说明,本次实验环境为Laravel5.2,不过本次讨论应该可以为你提供一个思路。

说在最前面
Laravel本身会自动为我们提供了CSRF校验,主要体现在 app/Http/Kernel.php 文件中。

protected $middlewareGroups = [
    'web' => [
        //...省略
        \App\Http\Middleware\VerifyCsrfToken::class,
    ],
    //...省略
];

如果你的应用不需要VerifyCsrfToken的保护,那就可以将此处注释掉即可,呵呵。不过既然你想要去使用Laravel提供的这个简单易用的工具,那就往下看吧~

SYNC表单提交
SYNC也就是同步表单提交,即下面这种表单提交的方式,注意到其中有一个特殊的名称为_token的隐藏域,当点击”提交”后,Laravel的VerifyCsrfToken服务会自动校验我们的_token字段,如果不正确则会抛出Illuminate\Session\TokenMismatchException错误。

<form action="{{ url('/post/something') }}" method="post">
        <input name="username" type="text" />
        <input name="password" type="password" />
        <input name="_token" type="hidden" value="{{ csrf_token() }}" />  
        <input type="submit" value="提交" />  
</form>

ASYNC Ajax X-CSRF-TOKEN
在Web2.0时代,如果都是上面的那种提交方式的话,会不会有点太low了,所以接下来这种应用场景说白了也就是平时的Ajax请求方式,和传统FORM提交方式类似,当需要在Ajax中应用CSRF校验的时候也需要设置一个类似于token的东西,在这里叫做X-CSRF-TOKEN,只不过此时的token需要放在当前请求的header中,具体做法如下:

在公共父页面的添加如下html

<meta name="csrf-token" content="{{csrf_token()}}" />

在公共的JavaScript代码添加如下代码
通过设置如下代码即可保证每一个Ajax请求都会将生成的token添加到header中,Laravel即可到header中找到X-CSRF-TOKEN,则可以进行验证。
注意,这只是jquery的设置方式,其他框架自行参考~

<script type="text/javascript">
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
    });
</script>

一个典型的X-CSRF-TOKEN验证流程
QQ20160807-1@2x

X-XSRF-TOKEN
X-XSRF-TOKEN有些类似于X-CSRF-TOKEN,不同点在于两者的token值的来源,比如X-CSRF-TOKEN的token值是通过{{csrf_token()}}来生成的,而X-XSRF-TOKEN的token是设置在cookie中的,即PHP将token生成好并且通过setcookie()将token设置,当发起请求时,JavaScript需要从cookie中找到该token,并设置到请求的header中。

一个典型的X-XSRF-TOKEN验证流程
QQ20160807-2@2x

X-XSRF-TOKEN是Laravel CSRF验证中优先级最低的
为什么这么说?我们看这段tokensMatch()方法即可明了,从代码中即可发现,判断优先级为 SYNC表单提交 > Ajax X-CSRF-TOKEN > X-XSRF-TOKEN。

protected function tokensMatch($request)
{
    $sessionToken = $request->session()->token();

    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

    if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
        $token = $this->encrypter->decrypt($header);
    }

    if (! is_string($sessionToken) || ! is_string($token)) {
        return false;
    }

    return hash_equals($sessionToken, $token);
}

绕过CSRF验证

前面讨论了那么多的这验证,那验证,但是却没有讨论如何去绕过验证,上面其实我已经说过了,最简单的绕过验证就是不使用VerifyCsrfToken它即可,也就是找到Kernel.php中的它然后注释掉,然而这么简单易用的功能如果放弃掉也是很可惜的,但是如果一定要使用VerifyCsrfToken而且还要绕过验证又该怎么搞?方法很简单,打开app/Http/Middleware/VerifyCsrfToken.php,将需要绕过验证的路由添加到$except数组中即可,比如

protected $except = [
    'post/something',
];

这样,即可以绕过验证了。

综上
总体来说,Laravel提供的CSRF是比较简单易用的,虽然验证的种类很多,但X-XSRF-TOKEN这种验证方式是属于相对比较麻烦的一种,而且从Laravel源码中即可感觉到它的优先级也是比较低的,哈哈哈,所以根据我们的应用类型有选择性的选择即可。

Laravel之CSRF
Tags:     

发表评论

电子邮件地址不会被公开。 必填项已用*标注