Laravel如何具体构建和检查CSRF令牌?

21
我在我的公共网站上使用了Laravel的CSRF保护。然而,由于Laravel使用会话来维护这个保护,我担心用户可能会离开他们的电脑,然后返回到以前留下的页面,发现ajax请求不起作用了。这是因为会话已经超时(令牌不再有效?)。如果这些用户是“已登录”用户,那么我可以简单地将它们重定向回登录页面。由于他们是公共用户,所以用户被迫刷新页面才能让它重新工作(很麻烦)。
或者我对此是错误的吗?CSRF令牌是否仍然会被Laravel验证(即使会话已经超时,页面仍然会发送令牌...但是Laravel会怎样处理它?)。最佳解决方案是让令牌部分基于时间戳,以便我们可以给令牌设置过期限制,而不是仅仅使用会话时间限制。我可以让我的CSRF令牌持续2天(因此只有那些走了2天的用户会返回到无法工作的页面)。
最终,这带给我一个问题:Laravel框架中处理这个问题的具体代码在哪里?我目前正在寻找它。此外,是否有一个容易替换的解决方案,或者我必须创建自己的版本csrf_token();来输出到我的页面上,然后我需要创建自己的路由过滤器与之配合。

https://github.com/laravel/framework/blob/5.6/src/Illuminate/Session/Store.php#L74 - Ryan
2个回答

26

Laravel只是为您简化操作,将令牌存储在会话中,但实际上代码是您自己的(可以根据需要进行更改)。查看 filters.php ,您应该会看到:

Route::filter('csrf', function()
{
    if (Session::token() != Input::get('_token'))
    {
        throw new Illuminate\Session\TokenMismatchException;
    }
});

它告诉我们,如果你有一条路线:

Route::post('myform', ['before' => 'csrf', 'uses' => 'MyController@update']);

当用户会话过期时,它会引发一个异常,但您可以自己处理,将自己的令牌存储在您认为更好的地方,而不是抛出异常,将您的用户重定向到登录页面:

Route::filter('csrf', function()
{
    if (MySession::token() != MyCSRFToken::get())
    {
        return Redirect::to('login');
    }
});

是的,你可以创建自己的csrf_token(),你只需要在Laravel之前加载它。如果你查看Laravel源代码中的helpers.php文件,你会发现它仅在该函数不存在时才创建该函数:

if ( ! function_exists('csrf_token'))
{
    function csrf_token()
    {
       ...
    }
}

1
啊...我知道这段代码,但我一直在深入研究框架,试图了解实际进行的计算。但你说得对,我们显然可以看到这里正在使用会话来防止 CSRF 攻击。这让我想到 CSRF 保护的典型用法是针对已登录用户的。然而,我在网站的不同公共区域发送 JSON 数据,我不希望其他人轻易地获取并尝试将该网站用作某个公共 API。 - prograhammer
而且...你提醒我可以覆盖那些辅助程序,真的帮了我很大的忙。所以最保险的方法是重写它,并将这些令牌从会话中删除。再次感谢你,Antonio! - prograhammer

4

由于这已经成为一个流行的问题,我决定发布我的具体解决方案,这个方案一直运作得非常好...

很可能你会有一个header.php或者其他部分视图,你在所有页面的顶部使用它,请确保它在<head>部分中:

<meta name="_token" content="<?=csrf_token(); ?>" />  

在您的 filters.php 文件中:

Route::filter('csrf', function() 
{
   if (Request::ajax()) {
        if(Session::token() != Request::header('X-CSRF-Token')){
            throw new Illuminate\Session\TokenMismatchException;
        } 
    }
});

在您的routes.php文件中

Route::group(array('before' => 'csrf'), function(){

    // All routes go in here, public and private

});

2
只是添加一点信息,如果您正在使用ajax调用,可以使用以下内容jQuery.ajaxSetup({ headers: { 'X-CSRF-Token' : $('meta[name=_token]').attr('content') } }); 来源 - Michel Ayres
是的。这就是我在Laravel 5中所做的。现在它实际上已经在Laravel文档中提到了。 :-) - prograhammer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接