Laravel 会话随机过期

Laravel session expires randomly

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

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

我们也有一些 AJAX post 请求,由于这种情况,我们遇到了失败的 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',
);

经过我们公司的大量调查,我得出了这个结果,希望对您有所帮助,首先是我们的环境规范:

  • PHP: 5.3.3
  • LARAVEL: 4.1
  • OS: centos 6 在服务器上 os x mavericks 在开发环境中
  • 阿帕奇:2
  • MYSQL: 5.6.19
  • REDIS:2.4.10
  • PREDIS:0.8.*

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

1.过期会话

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

2。 session过期时的并发请求数

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

3。登录并发请求

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

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

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

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

4。浏览器

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

5. Redis 错误

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

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

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

清除域 cookie 适用于我遇到的情况。

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

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

这可能与这个 well-known 问题有关

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

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