PHP会话提前到期

时间:2011-09-21 08:52:17

标签: php session

我在运行的PHP网站遇到问题,几分钟后用户正在注销(确切的时间会有所不同,但经常会出现问题),无论他们是否积极参与是否使用该网站。

困难在于我无法重现这个问题,如果我使用相同的浏览器登录相同的用户我没有注销,这表明这不是网站完全被破坏的情况。不幸的是,我无法访问用户计算机来运行任何流量嗅探软件。

我已经检查过的事情是:

  • 要求用户尝试不同的浏览器。这似乎并没有解决问题,也不是一个长期的解决方案,因为我无法决定客户将使用哪些浏览器。
  • 服务器时间正确且符合用户计算机。
  • 用户Apache运行时有权写入会话文件夹,我可以看到正在创建的会话文件及其修改时间正在更新。
  • 没有使用输出缓冲功能。
  • 问题发生在各种各样的页面上,似乎没有任何共同之处(也就是说,并非所有页面都使用AJAX,或更新数据库或其他原因)。
  • 用户只能从一台机器上访问他们的帐户,即他们不会在他们的笔记本电脑上做一些工作,切换到桌面,然后想知道为什么他们已经在他们的笔记本电脑上注销了(我们不允许多个同时登录同一用户)。

PHP中的会话设置是Debian默认值,并且未在.htaccess文件或其他任何位置更改。主要是:

session.cookie_lifetime    0
session.gc_divisor    100
session.gc_maxlifetime    1440
session.gc_probability    0
session.save_handler    files
session.save_path    /var/lib/php5
session.use_cookies    On

Debian通过cron作业删除会话而不是使用PHP的垃圾收集器,这就是为什么gc_probability被设置为0.我们运行的PHP版本是:PHP 5.2.6-1 + lenny13与Suhosin-Patch 0.9.6.2 (cli)(Lenny的最新版本,我们很快就会升级到Squeeze,但我不认为这是导致问题的原因。)

我们使用Zend_Session来管理会话,并在每个页面上创建一次Zend_Session_Namespace实例,从而自动调用session_start()。通过在注销页面上调用Zend_Session :: destroy()来清除会话,因此用户应该注销的唯一方法是:

  • 如果他们明确点击了注销链接(我们会在发生这种情况时进行记录,而且似乎不会出现浏览预先获取页面并因此将用户注销的情况)。
  • 如果他们让会话处于非活动状态超过24分钟,那么Debian可能会删除他们的会话(有一个cron作业每半小时运行一次,删除所有未修改超过24分钟的会话)。
  • 如果他们关闭浏览器,将删除其到期时间为0的会话cookie。

检查用户是否已登录的检查是:

  • 他们有一个有效的会话(通过查看我们是否可以访问$ zsession-> user_id来检查)。
  • 会话表中有一行具有匹配的用户ID和会话ID,这是在不到一小时前最后更新的。我们在注销时删除了这一行,这样即使会话仍然存在于磁盘上,也没有人可以在不登录的情况下访问该帐户。

有人可以提出我可以尝试的其他事项吗?

编辑:我根据评论留下的其他一些事情:

  • 设置session.cookie_domain:这似乎在PHP中有非常奇怪的行为。如果我没有设置此变量并将其保留为默认值''(空字符串),则对www.domain.com的请求将生成www.domain.com的cookie。但是,如果我将cookie_domain设置为“www.domain.com”,则cookie的域名为“.www.domain.com”(注意引导点,这意味着对www.domain.com以下的所有内容都有效,例如subsite.www .domain.com)。
  • 设置session.cookie_lifetime:PHP似乎没有更新每个请求的到期时间,因此如果我将cookie_lifetime设置为3600,cookie将在用户首次访问该网站一小时后到期,即使他们登录并经常使用它

编辑2:基于人们提出的其他问题:

  • 该网站托管在数据中心的单独VLAN中。访问该站点的任何人都不在该站点的同一网络上。
  • 没有使用IP身份验证,也没有在会话过程的任何部分使用客户端的IP地址(例如,我们不会将会话附加到IP地址,并阻止用户,如果他们的下一个请求来自不同的IP)。

8 个答案:

答案 0 :(得分:3)

  

Debian通过cron作业删除会话,而不是使用PHP的垃圾收集器

这很奇怪 - 在cron工作中运行的代码是什么?

  

我们在退出时删除了这一行

我建议您在会话过期/删除后2天保留此项(但在当前删除它时将其标记为已死)。此外,开始在Web服务器日志中记录会话ID。

答案 1 :(得分:1)

我认为你计算改变了

的价值
session.gc_maxlifetime 

我也遇到了同样的问题。我花了很多时间在上面,然后我问我的网络服务提供商,当他获得许可后,他改变了这个价值。现在它工作正常。

答案 2 :(得分:1)

是否有其他php应用程序在同一系统上运行(例如,在不同的vhost下?)?他们还在/ var / lib / php5中保存会话吗?

如果是这样,其中一个应用程序的会话垃圾回收阈值较低,它们会丢弃应用程序的会话文件。

我做了很多ZF开发,如果我使用基于文件系统的会话,我会将它们粘贴在应用程序/数据/会话中而不是系统默认值。

答案 3 :(得分:1)

最后,答案是废弃会话并编写我自己的非常简单的cookie代码,它与会话有以下不同之处:

  1. 在数据库中存储散列(类似于会话ID),而不是存储在文件中。
  2. 将Cookie设置为从现在起3600秒内到期(在每页上更新)而不是0秒(后者似乎会给IE用户带来问题,尽管我永远无法复制它。)
  3. 仅在用户登录或登录时发送cookie标头。
  4. 这不是一个理想的情况,因为有一些重新发明的轮子,但我的小解决方案似乎适用于PHP会话没有,并有一个工作网站是最重要的事情。

答案 4 :(得分:0)

您的网站位于不同的域名吗?例如domain.com,www.domain.com,subdomain.domain.com?如果某些页面被重定向到不同的域(www被认为是一个不同的子域),那么当地址改变时会话将不起作用

编辑: 您必须重现该问题。询问您的客户他们使用什么类型的浏览器,他们做什么行动,直到他们被抢劫,他们是否正在查看与您相同的网站IP? (即你们都在外部网络中,或者两者都在与网站相同的网络中)

当您设法找到问题时,请检查会话工作时的请求/响应标头,以及它何时不起作用然后进行比较。

答案 5 :(得分:0)

你的session.gc_maxlifetime设置为1440毫秒,仅为1.44秒。不应该是1440000毫秒= 24分钟?

答案 6 :(得分:0)

您可以尝试将session.use_only_cookies设置为1,将session.cookie_lifetime设置为1440秒。

答案 7 :(得分:0)

最后,我选择在每个页面请求上发送一个Set-Cookie标头,类似于FlyBy在其中一条评论中建议的内容。现在相关的代码/逻辑是(假设已经调用了session_start()):

$this->session_name = session_name();
$update_cookie = isset($_COOKIE[$this->session_name]); // Check if cookie already set, as PHP will send the first Set-Cookie when the session is started
$this->logged_in = $this->checkSession(); // Function which checks whether a valid (i.e. not timed-out) session row exists in the DB
if ($this->logged_in) {
  $this->updateSession(); // Update the session row to the current time

  if ($update_cookie) {
    // Update the cookie expiry only if it existed before the login check
    setcookie($this->session_name, $_COOKIE[$this->session_name], $this->time + 3600, '/');
  }
}

我不确定为什么会这样,但我没有任何进一步的抱怨,并且登录次数大幅下降(相同用户的日志中不再有几次登录在几分钟之内)。

但是,我可能会在某些时候重写代码,只是在客户端上使用数据库行和cookie,因为PHP中的会话功能有很多变量,很难找出造成问题的原因,会话cookie处理与正常cookie的处理方式略有不同。特别是,你必须小心setcookie函数,因为PHP启动的会话cookie的默认路径是'/',但setcookie的默认路径是当前目录路径,这些路径不一定相同。