11

我有一个网站的问题,其中 PHP 不使用 Internet Explorer 为特定用户保存会话变量。但是对于其他一些使用 Internet Explorer 的用户来说完全没有问题,使用其他浏览器的用户也没有任何问题。

我创建了以下三个小脚本,以确保网站中不涉及其他代码:

测试.php:

<?php
session_start();

function logMsg($text) {
    $filename = dirname(__FILE__) . "/test.log";
    $fh = fopen($filename, "a") or die("Could not open log file.");
    fwrite($fh, date("d-m-Y, H:i")." - $text\n") or die("Could not write file!");
    fclose($fh);
}

ob_start();
var_dump(session_id(), $_SESSION, $_SERVER, $_REQUEST);
$content = ob_get_clean();

logMsg("test.php");
logMsg($content);

$_SESSION['test'] = array('test' => 'lalala');
$_SESSION['count'] = 1;
?>
<a href="test2.php">Next</a>

测试2.php:

<?php
session_start();

function logMsg($text) {
    $filename = dirname(__FILE__) . "/test.log";
    $fh = fopen($filename, "a") or die("Could not open log file.");
    fwrite($fh, date("d-m-Y, H:i")." - $text\n") or die("Could not write file!");
    fclose($fh);
}

ob_start();
var_dump(session_id(), $_SESSION, $_SERVER, $_REQUEST);
$content = ob_get_clean();

logMsg("test2.php");
logMsg($content);

$_SESSION['count']++;
?>
<a href="test3.php">Next</a>

测试3.php:

<?php
session_start();

function logMsg($text) {
    $filename = dirname(__FILE__) . "/test.log";
    $fh = fopen($filename, "a") or die("Could not open log file.");
    fwrite($fh, date("d-m-Y, H:i")." - $text\n") or die("Could not write file!");
    fclose($fh);
}

ob_start();
var_dump(session_id(), $_SESSION, $_SERVER, $_REQUEST);
$content = ob_get_clean();

logMsg("test3.php");
logMsg($content);

的预期输出var_dump($_SESSION)将类似于:

array(0) {
}

array(2) {
    ["test"] => array(1) {
        ["test"] => string(6) "lalala"
    },
    ["count"] => int(1)
}

array(2) {
    ["test"] => array(1) {
        ["test"] => string(6) "lalala"
    },
    ["count"] => int(2)
}

但是,有问题的用户的输出如下:

array(0) {
}

array(0) {
}

array(1) {
    ["count"] => int(1)
}

这意味着不会为这些用户存储会话变量。但是,对于所有 3 个测试页面,有问题的用户的会话 ID 是相同的。

有人知道这可能是什么吗?据我所知,有问题的代码已经运行了好几年,问题在上个月左右开始出现。

编辑

对评论中问题的回答:

  • 我无法在本地机器上复制问题。
  • 我收到了来自 IE7 和 IE9 用户的问题报告。但我不能肯定地说其他版本没有问题,因为可能根本没有报告这些问题。
  • 有问题的用户的浏览器没有禁用 cookie,PHPSESSID cookie 被发送到服务器。
  • 机器名称中没有 - 或 _ ( https://stackoverflow.com/a/306601/534109 )。
  • 使用 session_regenerate_id() 重新生成会话 ID 对有问题的用户的结果没有影响。
  • 有问题的用户的时区和时间设置与服务器上的相同。

编辑 2

正如@nl-x 在评论中所述,数据存储在第二个请求中。因此,我调整了测试场景并添加了另一个步骤,以查看会话在后续请求中是否有效。情况就是这样。会话数据在请求之间设置step2.phpstep3.php保存。

所以现在的问题是为什么第一个请求的会话数据会丢失而不是后续请求?

4

4 回答 4

5

我发现有问题的用户都安装了 Chrome Frame。我通过在本地机器上安装 Chrome Frame 验证了这一点,在这种情况下,我能够复制问题。

问题是由于我们的服务器安装了 Suhosin 造成的。启用了以下 Suhosin 设置:

suhosin.session.cryptua
suhosin.cookie.cryptua

这意味着用户代理字符串也是用户会话标识的一部分。通常这不是问题,但对于安装了 Chrome 框架的用户,用户代理字符串在第一个请求和后续请求之间是不同的。禁用这些 Suhosin 设置后,就没有更多问题了。

于 2013-05-30T18:57:14.680 回答
2

我将假设这个,而不是等待具有 PHP 会话机制特定知识的人:

我主要使用 ASP.NET,并且该Session对象使用 cookie 来跨请求保存数据。如果 PHP 以同样的方式工作,最明显的结论是会话问题的用户要么禁用了 cookie,要么正在使用只允许白名单域设置 cookie 的软件。我会看看我是否能找到任何事实来支持这个理论......

从 PHP 手册(http://www.php.net/manual/en/intro.session.php):

这要么存储在用户端的 cookie 中,要么在 URL 中传播。

于 2013-05-29T08:31:12.920 回答
1

我无法准确告诉您为什么在第一次请求时/之后,cookie 似乎丢失了。(这就是我猜正在发生的事情。)以及为什么在第二个请求之后/之后不会丢失。

也许确实是一个缓存问题。检查开发人员工具,并查看网络选项卡中到底发生了什么。第一个请求是否带有 200 - OK,响应是否包含 cookie 标头?或者它确实被缓存了,正如评论之一所建议的那样?

但最后你应该真正实现正确的会话 id 传递(阅读它)。这适用于不想或不能处理 cookie 的人。

基本上它意味着改变:

<a href="test3.php">Next</a>

进入:

<a href="test3.php?<?php echo htmlspecialchars(SID); ?>">Next</a>

或者:

启用 --enable-trans-sid

现在,当 PHP 注意到会话没有通过 cookie 传递时,它将以不太安全的方式在 URL 中传递它们。特别是在这种情况下,您需要session_regenerate_id().

编辑: 哦,是的,我想早点提到它,但后来认为不可能。但转念一想我还是会提到它!:

默认情况下,Cookie 是特定于域的。如果用户访问http://yourdomain.com(没有www.),而第二个请求访问http://www.yourdomain.com,则 cookie 将无法在域更改后继续存在!从而影响你的会话。

要解决此问题,请设置会话 cookie 域,或始终使用相同的域(带或不带 www)。

于 2013-05-29T08:58:31.170 回答
-1

首先,您应该验证您的 php.ini 会话配置,尤其是 cookie 持续时间。在您的问题中添加部分。在给您错误的客户端上安装 Fiddler 并生成会话的完整 http 转储。这应该可以帮助您轻松追踪问题。

于 2013-05-29T09:19:47.647 回答