简体   繁体   English

Laravel会话随机到期

[英]Laravel session expires randomly

We had this issue on our website that we got CSRF errors randomly from our users. 我们在我们的网站上遇到过这个问题,我们从用户那里得到了CSRF错误。 The session cookie and session data were set to be expired in 12 hours and session driver is set to use Redis. 会话cookie和会话数据设置为在12小时内过期,会话驱动程序设置为使用Redis。 Further to our investigations we finally succeed to simulate the exception condition, so here is the scenario: 在我们的调查之后,我们最终成功地模拟了异常情况,所以这里是场景:

A user opens two different pages on the site, using Chrome browser with “open last closed tabs” setting on. 用户在网站上打开两个不同的页面,使用Chrome浏览器打开“打开上次关闭的标签”设置。 One of the pages has a form on it (eg 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. 他第二天重新打开浏览器(12小时过去,因此会话cookie和会话数据已过期)Chrome尝试重新加载所有打开的页面。 It sends two simultaneous requests to the server while none of them has session cookie. 它向服务器发送两个同时发出的请求,而它们都没有会话cookie。 At the server end Laravel generates two different session ID for each. 在服务器端,Laravel为每个会话ID生成两个不同的会话ID。 Chrome receives them and overrides one on the other session cookie. Chrome接收它们并覆盖其他会话cookie上的一个。 Once the user attempts to submit the form (eg login), it generates CSRF error as the form session cookie is overridden. 一旦用户尝试提交表单(例如登录),它就会在表单会话cookie被覆盖时生成CSRF错误。

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? 有没有人有任何想法我们如何解决这个问题?

PS: 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',
);

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 PHP:5.3.3
  • LARAVEL: 4.1 LARAVEL:4.1
  • OS: centos 6 on server and os x mavericks in development environment 操作系统:服务器上的centos 6和开发环境中的os x mavericks
  • APACHE: 2 APACHE:2
  • MYSQL: 5.6.19 MYSQL:5.6.19
  • REDIS: 2.4.10 REDIS:2.4.10
  • PREDIS: 0.8.* PREDIS: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. 首先,似乎TokenMistmatch异常发生在各种不同的条件下,我几乎调查了所有这些异常,并且能够解决其中的一些,一些取决于会话背后的逻辑,一些可能是错误。 In the following i will explain each situation that I have faced. 在下文中,我将解释我面临的每种情况。

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. 假设您已经将会话配置为3个小时,用户打开表单并出于某种原因离开计算机(获得一杯咖啡),因此在会话过期3小时后,他尝试提交表单并获取令牌例外。 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. 这就是为什么每个人一旦进入同时获得令牌错误,无论应用程序有多稳定,我可以设想两种方法来防止它,并且他们使用ajax及时更新会话cookie,或者增加会话过期时间相当长的时间。

2. Concurrent requests when session is expired 2.会话过期时的并发请求

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

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. 当登录发生时,出于安全原因,laravel会更改会话ID,复制会话数据和DESTROYS THE LAST SESSION,因此在登录并发请求时会出于某种原因说(多次单击登录按钮),以便重新生成会话ID多次和DESTROYS在服务器端最后生成的会话,因为其中一些请求仍然使用先前的会话ID(在服务器端不再存在)它导致重新生成CSRF令牌,请注意通常laravel不如果可以在guest虚拟机(未登录)会话中找到令牌,则在登录时重新生成令牌,但在这种情况下,因为guest虚拟机会话被销毁,因此它将重新生成令牌,并且可能导致其他请求中的令牌异常。使用原始令牌。 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 : 我在lauminvel代码中更改了一行,在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. 将true传递给会话存储的migrate方法将导致在迁移后销毁服务器上的会话,因此现在数据将保留在服务器上并在其到期时间而不是在此请求上销毁,它将解决问题。 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. 我不知道我们是否可以将其称为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 bug

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. 我们服务器上的一些令牌例外是因为redis,因为某些原因,laravel在打开会话时无法从redis服务器读取会话数据,因此会导致CSRF令牌的重新生成。 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. 我尝试过数据库,apc和文件驱动程序,没有人产生这个问题。 I have not yet found what is causing the bug but i think it can be a bug with redis or the predis library. 我还没有找到导致错误的原因,但我认为它可能是redis或predis库的错误。 (as you know laravel 4.1 is not using the latest version of predis because of compatibility issues). (如您所知,由于兼容性问题,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. 如果您遇到类似事件或者您有新事物,请与我分享。

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适用于我看到的场景。

In my case this was an issue with the cached config file. 在我的情况下,这是缓存配置文件的问题。 Laravel creates a config file which is cached inside bootstrap/cache folder. Laravel创建一个缓存在bootstrap / cache文件夹中的配置文件。 Rename this config.php file to something else and run "php artisan cache:clear" 将此config.php文件重命名为其他内容并运行“php artisan cache:clear”

Then run the site and it should work fine 然后运行该网站,它应该工作正常

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 - Asyncronous 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). 如果多个请求尝试覆盖会话并且会话文件没有锁定,则可能导致会话文件损坏(覆盖)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM