在Laravel中处理过期的令牌

Jam*_*sir 37 php laravel laravel-5

在laravel 5中处理过期令牌的最佳方法是什么?

我的意思是我有一个页面,它有一些执行ajax请求的链接.当页面加载时它们工作正常,但是当我等待一段时间后,我得到一个TOKEN MISMATCH错误.

现在,我必须刷新页面以使其再次工作.但是,我不想刷新页面.我想要一些方法刷新令牌或其他一些工作来修复它.

我希望你明白我的观点.

UX *_*abs 24

解决它的方法是,每隔一定时间实际获取新令牌,否则你就是在破坏csrf令牌的目的:

<html>
    <head>
        <meta name="csrf_token" content="{{ csrf_token() }}">
    </head>
    <body>
        <script type="text/javascript">
            var csrfToken = $('[name="csrf_token"]').attr('content');

            setInterval(refreshToken, 3600000); // 1 hour 

            function refreshToken(){
                $.get('refresh-csrf').done(function(data){
                    csrfToken = data; // the new token
                });
            }

            setInterval(refreshToken, 3600000); // 1 hour 

        </script>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

在laravel路线

Route::get('refresh-csrf', function(){
    return csrf_token();
});
Run Code Online (Sandbox Code Playgroud)

我在任何语法错误的情况下道歉,长时间没有使用jquery,但我想你明白了

  • 这写成了失败csrf_token的观点.您应该传递当前令牌,并且只有旧令牌有效时才返回新令牌.否则,恶意的人可能只是点击refresh-csrf路由获取令牌并使用它来发出通常会受到保护的任何请求. (35认同)
  • 我在这里错过了什么@jfadich并评论upvoters?只是能够向csrf端点发出请求并不能让攻击者找到阻止csrf保护的方法.攻击者仍然需要以某种方式将令牌放入用户的会话中(例如使用SOP错误或XSS漏洞),因此我不知道上述注释的含义. (18认同)
  • 这个解决方案是可行的,特别是对于我们这些使用 `$.ajaxSetup({ headers: {'X-CSRF-TOKEN': csrfToken} });` 但是,你也应该 `$("input[name=_token] ").val(data);` (2认同)
  • @jfadich,您能否详细说明“恶意人员”如何攻击受身份验证保护的“refresh-csrf 路由”?请分享你的智慧! (2认同)
  • @berrunder,当我说“身份验证保护”时,我确实意味着身份验证保护。在任何 csrf 可以添加到任何内容之前,您对 auth-protected 路由的请求将下降。刷新 csrf 用于不将您的输入发送到过期无效,以防用户仍然处于活动状态。 (2认同)
  • @JaredChu 使用该答案中的示例 `cute-cat-pictures.org` 可以向 `mybank.com/refresh-csrf` 发出请求以获取有效令牌。就像那个答案说的那样,您的浏览器将为“mybank.com”发送适当的 cookie,因此攻击者将获得有效的令牌。然后,他们可以使用该令牌向 `http://www.mybank.com/transfer?to=123456;amount=10000` 发出请求。 (2认同)

Rya*_*yan 22

我认为@UX Labs的回答是误导性的.然后@jfadich的评论似乎完全不正确.

对于2017年5月的Laravel 5.4,我用这种方式解决了这个问题:

这是一个有效的答案

web.php:

Route::post('keep-token-alive', function() {
    return 'Token must have been valid, and the session expiration has been extended.'; //https://stackoverflow.com/q/31449434/470749
});
Run Code Online (Sandbox Code Playgroud)

在您的视图中的javascript中:

$(document).ready(function () {

    setInterval(keepTokenAlive, 1000 * 60 * 15); // every 15 mins

    function keepTokenAlive() {
        $.ajax({
            url: '/keep-token-alive', //https://stackoverflow.com/q/31449434/470749
            method: 'post',
            headers: {
                'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
            }
        }).then(function (result) {
            console.log(new Date() + ' ' + result + ' ' + $('meta[name="csrf-token"]').attr('content'));
        });
    }

});
Run Code Online (Sandbox Code Playgroud)

请注意,您不得'keep-token-alive'在其中的排除项中列出VerifyCsrfToken.php.正如@ ITDesigns.eu在评论中暗示的那样,对于此路由来说,验证当前是否存在有效令牌并且只需要延长其到期时间非常重要.

为什么这种方法解决了我的问题

我的Laravel网站允许用户观看视频(一小时),并使用ajax每分钟发布一次进度.

但是许多用户加载页面,然后在几个小时之后才开始播放视频.

我不知道他们为什么在观看之前就把他们的浏览器标签打开了,但是他们确实如此.

然后我会在我的日志中获得大量的TokenMismatch异常(并且会错过他们进度的数据).

session.php,我'lifetime'从120分钟改为360分钟,但这仍然不够.而且我不想让它超过6个小时.所以我需要启用这一页来经常通过ajax扩展会话.

如何测试它并了解令牌的工作方式:

web.php:

Route::post('refresh-csrf', function() {//Note: as I mentioned in my answer, I think this approach from @UX Labs does not make sense, but I first wanted to design a test view that used buttons to ping different URLs to understand how tokens work. The "return csrf_token();" does not even seem to get used.
    return csrf_token();
});
Route::post('test-csrf', function() {
    return 'Token must have been valid.';
});
Run Code Online (Sandbox Code Playgroud)

在您的视图中的javascript中:

<button id="tryPost">Try posting to db</button>
<button id="getNewToken">Get new token</button>

(function () {
    var $ = require("jquery");

    $(document).ready(function () {
        $('body').prepend('<div>' + new Date() + ' Current token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
        $('#getNewToken').click(function () {
            $.ajax({
                url: '/refresh-csrf',
                method: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('meta[name="csrf-token"]').attr('content', d);
                $('body').prepend('<div>' + new Date() + ' Refreshed token is: ' + $('meta[name="csrf-token"]').attr('content') + '</div>');
            });
        });
        $('#tryPost').click(function () {
            $.ajax({
                url: '/test-csrf',
                method: 'post',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            }).then(function (d) {
                $('body').prepend('<div>' + new Date() + ' Result of test: ' + d + '</div>');
            });
        });


    });
})();
Run Code Online (Sandbox Code Playgroud)

session.php,暂时改为'lifetime'非常短的东西用于测试目的.

然后玩.

这就是我学习Laravel令牌如何工作以及我们如何经常成功地POST到受CSRF保护的路由以便令牌继续有效的方式.

  • 很简单,谢谢!axios更容易,因为每次请求都会自动发送最新的`XSRF-TOKEN`.你可以这样做:`setInterval(()=> axios.post('/ keep-token-alive'),1000*60*15);` (3认同)
  • 如果用户设备在会话期间进入睡眠状态,则这将不起作用,因为当用户睡眠时,“setInvall”也会进入睡眠状态:/sf/answers/444313551/ (2认同)

pau*_*dru 21

我为这种情况组合了两件事:

1.延长会话寿命

//In config/session.php replace this:

'lifetime' => 120

//with:

'lifetime' => 360
Run Code Online (Sandbox Code Playgroud)

Laravel 5默认生命周期为120(分钟),您可以将其更改为您喜欢的任何值,例如360(6小时)

2.捕获异常并显示错误消息

//In app/Exceptions/Handler.php replace this:

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    return parent::render($request, $e);
}

//with:

public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    if ($e instanceof \Illuminate\Session\TokenMismatchException) {            
        return redirect('/')->withErrors(['token_error' => 'Sorry, your session seems to have expired. Please try again.']);
    }

    return parent::render($request, $e);
}
Run Code Online (Sandbox Code Playgroud)

因此,基本上您将用户重定向到根"/"(您可以将其更改为您想要的任何路径)并显示错误消息,并且在该页面上您必须执行此操作以显示错误消息:

@if ($errors->has('token_error'))
    {{ $errors->first('token_error') }}
@endif
Run Code Online (Sandbox Code Playgroud)

  • 因此,当令牌过期时,您将用户重定向到主页,并显示“哈哈,你太晚了,slowpoke”消息?这很“方便”:) 想象一下用户在花费几个小时撰写文章并获得“有用”消息但失去所有工作后的满意度。 (2认同)

Ada*_*dam 13

根据文档

Laravel 会为应用程序管理的每个活动用户会话自动生成一个 CSRF “令牌”。

这意味着,对于任何个人,用户访问的任何页面的 csrf 代码都是相同的。一旦您的会话过期,它就会失效。因此,如果您将生命周期设置为 1 周,CSRF 令牌只会在 1 周后过期。

这可以像这样实现config/session.php

 /*
    |--------------------------------------------------------------------------
    | Session Lifetime
    |--------------------------------------------------------------------------
    |
    | Here you may specify the number of minutes that you wish the session
    | to be allowed to remain idle before it expires. If you want them
    | to immediately expire on the browser closing, set that option.
    |
    */

    'lifetime' => 60 * 24 * 7, // Set session lifetime to 1 week

    'expire_on_close' => true,
Run Code Online (Sandbox Code Playgroud)

为什么我不喜欢以上任何一个答案:

  1. 来自 UX 实验室的回答:

保持会话永远活着并token在固定时间后重新创建一个新的 CSRF 。如果用户打开多个水龙头,这是一个问题。每次点击刷新CSRF令牌时,所有其他选项卡都将无效。

  1. 瑞安的回答

这个答案更好,因为它不会更改 CSRF token,因此不会影响多个选项卡。它只是在使用setInterval. 但是,setInterval在 PC 进入睡眠状态时不起作用。因此,当 PC 进入睡眠状态时,会话可能会过期,这也是一种可能的情况。因此,与其尝试通过 js 调用保持会话处于活动状态,不如增加生命周期。

  1. 来自 paulalexandru 的回答

当会话超时时显示错误是可以的,但如果问题永远不会发生会更好。将生命周期设置为 6 小时是不够的,因为标签页很可能会打开几天。

  1. 其他答案

所有其他答案都建议为问题中的路由禁用 CSRF,但这当然不是选项,因为它会带来很大的安全风险。