Laravel 5 CSRF全局令牌隐藏字段,用于页面中的所有表单

时间:2015-02-13 13:17:56

标签: php laravel csrf

我最近迁移到Laravel 5,现在每次提交帖子时都会进行CSRF检查。我想要删除它,但我想遵循最佳实践,所以我会保持这种方式。

另一方面,我提交ajax请求时出现问题..我的页面有多个表单,有些提交甚至不是来自表单,只是简单的ajax调用。我的想法是在页面上有一个隐藏的“令牌”输入并将其附加到每个提交。拥有通用单一令牌输入有任何缺点吗?

另外,我该如何输出令牌?可以在页脚上创建一个隐藏的输入吗?

7 个答案:

答案 0 :(得分:24)

我没有看到任何缺点。您可以在布局文件中轻松创建全局标记字段:

<input type="hidden" name="_token" id="csrf-token" value="{{ Session::token() }}" />

或者如果您使用表单构建器:

{!! Form::token() !!}

在jQuery中,您可以使用类似this的内容将令牌附加到每个请求。

答案 1 :(得分:21)

在表单中添加表单令牌有helper。你可以使用

{!! csrf_field() !!}

在表格内。它将添加隐藏的输入和令牌。

答案 2 :(得分:5)

您可以在页面底部使用以下内容:

$('form').append('{{csrf_field()}}');

这将为您的所有forms附加一个隐藏的输入:

<input type="hidden" name="_token" value="yIcHUzipr2Y2McGE3EUk5JwLOPjxrC3yEBetRtlV">

对于你所有的AJAX请求:

$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        //////////// Only for your domain
        if (settings.url.indexOf(document.domain) >= 0) {
            xhr.setRequestHeader("X-CSRF-Token", "{{csrf_token()}}");
        }
    }
});

答案 3 :(得分:3)

以下是我的CSRF如何在我最近升级到使用Laravel 5的jQuery Mobile应用程序中的所有不同场景中工作的一些摘录:

我在一个变量中添加了一个加密的csrf标记,该变量将传递给我的主基础控制器中的视图: app\Http\Controllers\MyController.php

$this->data['encrypted_csrf_token'] = Crypt::encrypt(csrf_token());

然后,我在主视图标题中添加了元标记: resources\views\partials\htmlHeader.blade.php

<meta name="_token" content="{!! $encrypted_csrf_token !!}"/>

然后,我也按照一些论坛的建议添加了这个jquery片段:

    $(function () {
            $.ajaxSetup({
                    headers: {
                            'X-XSRF-TOKEN': $('meta[name="_token"]').attr('content')
                    }
            });
    });

但是,密钥(至少我的设置)是在我的自定义VerifyCsrfToken中间件中添加了对XSRF-TOKEN cookie的检查: app\Http\Middleware\VerifyCsrfToken.php:

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

            $header = $request->header('X-XSRF-TOKEN');

            $cookie = $request->cookie('XSRF-TOKEN');

            return StringUtils::equals($token, $request->input('_token')) ||
                   ($header && StringUtils::equals($token, $this->encrypter->decrypt($header))) ||
                    ($cookie && StringUtils::equals($token, $cookie));
    }

在我添加之前,由于TokenMismatchException,我的所有AJAX POST(包括表单提交和延迟加载列表视图)都失败了。

修改 第二个想法,我不确定将会话令牌与cookie中设置的会话令牌进行比较有多大意义(首先会出现会话令牌?)。这可能只是绕过了这一切的安全性。

我认为我的主要问题是上面的jquery片段应该是为每个ajax请求添加X-XSRF-TOKEN标头。在我的jQuery Mobile应用程序(特别是在我的lazyloader plugin)中,这对我来说并不适用,直到我为插件本身添加了一些选项。我添加了一个新的默认选择器csrf(在这种情况下为meta[name="_token"])和一个新的默认设置csrfHeaderKey(在这种情况下为X-XSRF-TOKEN)。基本上,在插件初始化期间,如果可以由_headers选择器(默认或用户定义)定位,则使用CSRF令牌初始化新的csrf属性。然后,在可以触发ajax POST的3个不同位置(当重置会话变量或延迟加载listview时)$ .ajax的headers选项设置为_headers中的任何内容。

无论如何,由于服务器端收到的X-XSRF-TOKEN来自加密的meta _token,我认为现在CSRF保护工作正常。

我的app\Http\Middleware\VerifyCsrfToken.php现在看起来像这样(基本上回到了Laravel 5 - LOL提供的默认实现):

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

            $_token = $request->input('_token');

            $header = $request->header('X-XSRF-TOKEN');

            return StringUtils::equals($token, $_token) ||
                   ($header && StringUtils::equals($token, $this->encrypter->decrypt($header)));
    }

答案 4 :(得分:2)

我认为你可以做这样的事情(如果我有机会,未经测试会更新)

$(document).on('submit', 'form', function(e)
      $(this).append('<input name="_token" value="{{{ Session::token() }}}">);
});

实际上,您可能希望将令牌存储在过期时重新更新的变量中。

将其附加到提交上的好处是,如果您通过ajax附加元素我认为它仍然可以工作而无需添加任何其他内容。

编辑:这是一篇关于将Rails UJS与Laravel一起使用的精彩文章(其中包含此自动CRSF令牌功能):https://medium.com/@barryvdh/unobtrusive-javascript-with-jquery-ujs-and-laravel-e05f444d3439

答案 5 :(得分:1)

您需要传递包含X-XSRF-TOKEN加密版本的标题csrf-token

我知道有两种方法可以做到这一点。您可以加密令牌并将其传递给视图:

$xsrfToken = app('Illuminate\Encryption\Encrypter')->encrypt(csrf_token());

return view('some.ajax.form.view')->with('xsrf_token', $xsrfToken);

或者您可以使用JavaScript从Cookie中获取令牌(Angular使这很简单)。在vanilla JS中你可能会做这样的事情:

function getCookie(name) {
    var pattern = RegExp(name + "=.[^;]*")
    matched = document.cookie.match(pattern)
    if (matched) {
        var cookie = matched[0].split('=')
        return decodeURIComponent(cookie[1])
    }
    return false
}

在jQuery中,您可以为ajax请求执行类似的操作:

$.ajax({
    // your request
    //
    beforeSend: function(request) {
        return request.setRequestHeader('X-XSRF-TOKEN', getCookie('XSRF-TOKEN'));
    }
});

答案 6 :(得分:0)

所有答案都不包括JS文件。如果我们不使用刀片而我们想使用 JS 文件怎么办。刀片语法在那里不起作用。

这里的代码适用于表单和ajax。

var csrf = document.querySelector('meta[name="csrf-token"]').content;
var csrf_field = '<input type="hidden" name="_token" value=“'+csrf+'”>';

$('form').append(csrf_field);

$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        if (settings.url.indexOf(document.domain) >= 0) {
            xhr.setRequestHeader("X-CSRF-Token", csrf);
        }
    }
});