Laravel 会话随机过期

Posted

技术标签:

【中文标题】Laravel 会话随机过期【英文标题】:Laravel session expires randomly 【发布时间】:2015-03-12 09:18:15 【问题描述】:

我们在我们的网站上遇到了这个问题,我们从用户那里随机收到了 CSRF 错误。会话 cookie 和会话数据设置为 12 小时后过期,会话驱动程序设置为使用 Redis。经过我们的调查,我们终于成功模拟了异常情况,所以下面是场景:

用户使用 Chrome 浏览器打开网站上的两个不同页面,并设置了“打开上次关闭的标签页”设置。其中一个页面上有一个表单(例如登录),然后用户在某个时候退出浏览器。他在第二天重新打开了他的浏览器(12 小时过去了,因此会话 cookie 和会话数据已过期)Chrome 尝试重新加载所有打开的页面。它同时向服务器发送两个请求,而它们都没有会话 cookie。在服务器端,Laravel 为每个生成两个不同的会话 ID。 Chrome 接收它们并覆盖另一个会话 cookie 上的一个。一旦用户尝试提交表单(例如登录),它会生成 CSRF 错误,因为表单会话 cookie 被覆盖。

我们还有一些 AJAX 发布请求,由于这种情况,我们收到了失败的 CSRF 错误。

我想知道 Laravel 是否可以以安全的方式为两个请求生成相同的会话 ID。

有没有人知道如何解决这个问题?

P.S:我们使用 laravel 4.1 和这个会话配置:

return array(

    'driver' => 'redis',

    'lifetime' => 720,

    'expire_on_close' => false,

    'files' => storage_path().'/sessions',

    'connection' => null,

    'table' => 'sessions',

    'lottery' => array(2, 100),

    'cookie' => 'laravel_session',

    'path' => '/',

    'domain' => '.ourdomain.com',
);

【问题讨论】:

这可能会有所帮助***.com/questions/27938186/… @astro 它只是删除了导致相同问题的最后一个会话。关于以安全方式生成相同会话 ID 的任何想法? 我想唯一的方法是扩展会话类并编写自己的? laravel.com/docs/4.2/extending#session 检查你是否无意中删除了.js中的cookie 【参考方案1】:

在我们公司对这个问题进行了很多调查后,我得出了这个结果,希望对您有所帮助,首先这里是我们的环境规范:

php:5.3.3 LARAVEL:4.1 操作系统:服务器上的 centos 6 和开发环境中的 os x mavericks 阿帕奇:2 mysql:5.6.19 REDIS:2.4.10 PREDIS:0.8.*

首先,似乎 TokenMistmatch 异常发生在各种不同的条件下,我几乎调查了所有这些并能够解决其中的一些问题,有些取决于会话背后的逻辑,有些可能是错误。下面我将解释我遇到的每种情况。

1.过期会话

假设您已将会话配置为 3 小时,用户打开了一个表单,出于某种原因他离开了计算机(喝了杯咖啡),因此在会话过期 3 小时后,他尝试提交表单并获得令牌异常。这就是为什么无论应用程序有多稳定,每个人都会遇到令牌错误,我可以想象两种方法来防止它,他们正在使用 ajax 及时更新会话 cookie,或者增加会话过期时间相当长的时间。

2。会话过期时的并发请求

有时在加载您的第一页并发请求时会发生,例如,用户在 chrome 上打开了两个不同的选项卡,当他重新打开 chrome 时,chrome 会同时发送请求,或者您可能在第一个加载时有多个并发 ajax 请求您的应用程序的页面。因此请注意,在收到第一个响应后会收到会话 cookie,但您可以在此之前发送下一个请求,因此您将在每个请求上获得一个新会话(新 cookie),如果其中一个可能导致令牌异常requests 填充表单。您可以根据其来源阻止这种情况,例如,如果 ajax 请求导致问题并且您没有在 ajax 响应中使用会话,则如果请求是 ajax,则可以禁用发送会话 cookie,在第二种情况下(单击多次提交按钮)您可以在提交后简单地禁用该按钮。

3.登录时的并发请求

当登录发生时,出于安全原因,laravel 会更改会话 ID,复制会话数据并销毁最后一个会话,所以当登录并发请求发生时(多次单击登录按钮)所以说会话 ID将多次重新生成并销毁服务器端最后生成的会话,因为其中一些请求仍然使用先前的会话 id(服务器端不再存在)它会导致重新生成 CSRF 令牌,请注意通常如果 laravel 可以在访客(未登录)会话中找到令牌,它不会在登录时重新生成令牌,但在这种情况下,由于访客会话被破坏,它将重新生成令牌,并且可能导致其他令牌异常使用原始令牌的请求。另请注意,此问题不仅会导致令牌异常,还可能导致用户在一个请求后注销,因为并发请求可以更改登录的会话。我通过在 Illuminate\Auth\Guard:updateSession 中更改 laravel 代码中的一行来解决了这个问题:

 protected function updateSession($id)
 
        $this->session->put($this->getName(), $id);

        //$this->session->migrate(true);
        $this->session->migrate()
 

将 true 传递给会话存储的 migrate 方法将导致迁移后销毁服务器上的会话,因此现在数据将保留在服务器上并在其过期时间而不是在此请求时销毁,它将解决问题。我不知道我们是否可以将此称为 laravel 中的错误,但我想我们可以为此提出更好的解决方案。

4.浏览器

在调查日志后发现,其中一些令牌异常是因为用户的浏览器不接受会话 cookie,我在 ios Safari 上看到最多,我相信这是我们无能为力的事情。

5. Redis 错误

我们服务器上的一些令牌异常是因为redis,由于某种原因laravel在打开会话时无法从redis服务器读取会话数据,因此会导致CSRF令牌的重新生成。它在我们的服务器上随机发生,所以我尝试更改会话驱动程序并且这种类型的异常消失了。我尝试了数据库、apc 和文件驱动程序,但没有一个产生这个问题。我还没有找到导致该错误的原因,但我认为这可能是 redis 或 predis 库的错误。 (你知道 laravel 4.1 因为兼容性问题没有使用最新版本的 predis)。

好的,这些是我过去 2 个月处理此问题的经验。我希望它可能会改变您对解决此问题的看法,最重要的是令牌异常不会发生,因为它可能是多个问题的结果。如果您有类似的事件或您有新的东西,请与我分享。

【讨论】:

@Amire 您好,您是否为场景 1 找到了更好的解决方案? @shangyeshen 不幸的是,我们最终增加了会话过期时间。【参考方案2】:

我多次遇到这个问题,没有具体原因,Amir 也没有提到。

清除域 cookie 适用于我所看到的场景。

【讨论】:

【参考方案3】:

就我而言,这是缓存配置文件的问题。 Laravel 创建一个缓存在 bootstrap/cache 文件夹中的配置文件。将此 config.php 文件重命名为其他文件并运行“php artisan cache:clear”

然后运行该站点,它应该可以正常工作

【讨论】:

【参考方案4】:

这可能与这个众所周知的问题有关

Laravel 5.0 - Asyncronous AJAX Requests cause Session Variable Changes to be Overwritten #7549

如果多个请求尝试覆盖会话并且会话文件没有锁定,则可能导致会话文件损坏(被覆盖)。

【讨论】:

以上是关于Laravel 会话随机过期的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 会话过期事件

Laravel 会话立即过期(Laravel 5.5)

会话过期的 laravel 事件监听器,因为会话过期时没有调用注销事件监听器

使用 Angularjs 和 Laravel 注销会话过期

如何在 Laravel 5.x 中注销过期会话?

当会话 cookie 在 Laravel 中过期时重新加载站点