Laravel 会话随机过期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27938723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 10:41:29  来源:igfitidea点击:

Laravel session expires randomly

phpsessionlaravellaravel-4

提问by Amir

We had this issue on our website that we got CSRF errors randomly from our users. The session cookie and session data were set to be expired in 12 hours and session driver is set to use Redis. Further to our investigations we finally succeed to simulate the exception condition, so here is the scenario:

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

A user opens two different pages on the site, using Chrome browser with “open last closed tabs” setting on. One of the pages has a form on it (e.g. login) then the user quits the browser at some point. He reopens his browser next day (12 hours is passed so session cookie and session data are expired) Chrome tries to reload all the pages which were opened. It sends two simultaneous requests to the server while none of them has session cookie. At the server end Laravel generates two different session ID for each. Chrome receives them and overrides one on the other session cookie. Once the user attempts to submit the form (e.g. login), it generates CSRF error as the form session cookie is overridden.

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

We also had some AJAX post requests which we got failed CSRF errors due to this condition.

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

I was wondering whether or not Laravel can generate the same session ID for both requests in a secure manner.

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

Does anyone have any ideas how we can fix this issue?

有谁知道我们如何解决这个问题?

P.S: we are using laravel 4.1 with this session configuration:

PS:我们在这个会话配置中使用 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',
);

回答by Amir

After a lot of investigation on the issue in our company, I came to this result, I hope it helps, first of all here is our environment specifications:

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

  • PHP: 5.3.3
  • LARAVEL: 4.1
  • OS: centos 6 on server and os x mavericks in development environment
  • APACHE: 2
  • MYSQL: 5.6.19
  • REDIS: 2.4.10
  • PREDIS: 0.8.*
  • PHP:5.3.3
  • 拉维尔:4.1
  • 操作系统:服务器上的 centos 6 和开发环境中的 os x Mavericks
  • 阿帕奇:2
  • MYSQL:5.6.19
  • Redis: 2.4.10
  • 预测值:0.8.*

First of all it seems that the TokenMistmatch exception occurs in a varied different conditions, I nearly investigated all of them and was able to solve some of them, some depends on the logic behind the session and some can be bugs. In the following i will explain each situation that I have faced.

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

1. Expired sessions

1. 过期会话

Let's say you have configured your session for 3 hours a user opens up a form and for some reason he leaves the computer (getting a cup of coffee) so after 3 hours when the session is expired he tries to submit a form and gets a token exception. this is why everyone once in while gets a token errror regardless of how much stable the application is, I can imagine of 2 ways to prevent it and they're renewing the session cookie using ajax on a timely basis, or increasing the session expire time to a considerable amount of time.

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

2. Concurrent requests when session is expired

2. session过期并发请求

Sometimes on the load of your first page concurrent requests happen, for example the user has two different tabs open on chrome and when he reopens chrome chrome sends the requests simultaneously or you may have multiple concurrent ajax request on the load of the first page of your application. so note that the session cookie is received after the first response is received but you may send the next request before this happens and therefore you will get a new session (new cookie) on each requests, this can lead to token exception if one of these requests populates a form. you can prevent this scenario based on it's source, for example if the ajax request are causing the problem and you do not use session in ajax responses, you can disable sending the session cookie if the request is ajax, in the second scenario (clicking the submit button multiple times) you can simple disable the button once it's submitted.

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

3. Concurrent requests on login

3. 登录并发请求

When login is occurred, laravel for security reasons changes the session id, copy the session data and DESTROYS THE LAST SESSION so let say for some reason when logging in concurrent requests happen (clicking the login button multiple times) so the session id would be regenerated multiple times and DESTROYS the last generated sessions in the server side, as some of these requests still use the prior session id (which does not exist in the server side anymore) it leads to regenerating the CSRF token, please note that normally laravel does not regenerate the token on login if it can find the token in guest (not logged in) session, but in this case as the guest session is destroyed as a result it will regenerate the token and it can result to token exception in other requests that are using the original token. also note that not only this issue can result in token exception it can also result in the user being logged out after one request, because the concurrent requests can change the logged in session. I solved this issue by changing one line in the laravel code, in Illuminate\Auth\Guard:updateSession:

另请注意,此问题不仅会导致令牌异常,还会导致用户在一次请求后被注销,因为并发请求会更改已登录的会话。我通过更改 Laravel 代码中的一行解决了这个问题,在Illuminate\Auth\Guard:updateSession

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

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

passing true to the migrate method of the session store will result in destroying the session on the server after migration, so now the data will remain on the server and destroyed on their expire time rather than on this request and it will solve the issue. I don't know if we can call this a bug in laravel, but I guess we can come up with a better solution for this.

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

4. Browsers

4. 浏览器

After investigating the logs it turned out that some of these token exceptions were because of the user's browser not accepting the session cookie,I saw it the most on iOS Safari, I believe this is something we can do nothing about it.

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

5. Redis bug

5.Redis错误

Some of the token exceptions on our sever was because of redis, for some reason laravel could not read the session data from the redis server when opening the session so it would result to the regeneration of the CSRF token. it was happening randomly on our server, so I tried changing the session driver and this type of exceptions faded away. I tried database, apc and file drivers and none produced this issue. I have not yet found what is causing the bug but i think it can be a bug with redis or the predis library. (as you know laravel 4.1 is not using the latest version of predis because of compatibility issues).

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

Okay these are my experiences of the last 2 month working on this issue. I hope it may change your point of view regarding solution to this issue, the most important thing is that the token exception does not happen because of one reason it's can be a result of multiple issue. please share with me if you have had similar incident or you have something new.

好的,这些是我过去 2 个月在这个问题上的工作经验。我希望它可以改变您对此问题的解决方案的观点,最重要的是令牌异常不会因为一个原因而发生,它可能是多个问题的结果。如果您遇到过类似的事件或有新的事情,请与我分享。

回答by mhughes

I've come across this issue various times, for no specific reason, and it's not mentioned by Amir.

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

Clearing the domain cookies works for the scenario I'm seeing.

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

回答by Sudharshan R

In my case this was an issue with the cached config file. Laravel creates a config file which is cached inside bootstrap/cache folder. Rename this config.php file to something else and run "php artisan cache:clear"

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

Then run the site and it should work fine

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

回答by Vong Panha Huot

This might be related to this well-known issue

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

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

Laravel 5.0 - 异步 AJAX 请求导致会话变量更改被覆盖 #7549

If multiple requests try to overwrite the session and the session files do not have a lock then it could cause the session file to become corrupted (overwritten).

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