为什么PHP不能在Internet Explorer中保存特定用户的会话变量?

11

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

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

test.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>

test2.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>

test3.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)
}

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

有人知道这是什么原因吗?据我所知,出问题的代码已经运行了几年,问题是最近一个月左右才开始显现的。

编辑

回答评论中的问题:

  • 我无法在本地机器上复制该问题。
  • 我收到来自使用IE7和IE9的用户报告的问题。但我不能确定其他版本没有问题,因为可能只是尚未报告。
  • 有问题的用户的浏览器未禁用cookie,PHPSESSID cookie已发送到服务器。
  • 机器名称中没有-或_(https://dev59.com/J3VC5IYBdhLWcg3wZwTj#306601)。
  • 使用session_regenerate_id()重新生成会话ID对于有问题的用户没有影响。
  • 具有问题的用户的时区和时间设置与服务器上的相同。

编辑2

正如@nl-x在评论中所述,数据将在第二个请求中存储。所以我修改了测试方案,并添加了另一个步骤以查看会话是否在随后的请求中起作用。结果是,step2.phpstep3.php中设置的会话数据在请求之间保存。

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


1
@GungFoo 怎么说呢?IE9+对标准的支持非常好。 - Tieson T.
1
@HamZaDzCyberDeV 似乎有大约30%的世界人口:http://www.sitepoint.com/browser-trends-march-2013/ - Tieson T.
3
特定用户是否禁用了cookie? - Phil Cross
2
如何防止会话劫持? - HamZa
3
这似乎是一些人抱怨会话不起作用的计算机上缓存问题。遗憾的是,你无法解决这个问题,如果你无法复制该问题,则最好检查他们的IE配置以及是否在代理后面。如果没有这些信息,你就无法创建修复程序,因为PHP方面一切都正常。 - N.B.
显示剩余17条评论
4个回答

5

我发现所有遇到问题的用户都安装了Chrome Frame。我在本地安装了Chrome Frame进行验证,结果成功复制了这些问题。

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

suhosin.session.cryptua
suhosin.cookie.cryptua

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


太棒了。这绝对不是我考虑过的事情,但它很有道理(也很好知道)。干得好。 - Tieson T.

2
我将代替等待具有PHP会话机制特定知识的人提出以下观点:
我主要使用ASP.NET,Session对象使用cookie在请求之间保留数据。如果PHP的工作方式相同,则最明显的结论是,会话问题的用户要么禁用了cookie,要么使用只允许设置白名单域的软件。我将查找是否有任何事实来支持这个理论...
从PHP手册中(http://www.php.net/manual/en/intro.session.php):

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


@TiesonT。是的,PHP的cookies工作方式与您描述的方式非常相似。 - Spudley
@Jan-Henk 数字。那肯定太容易了,不可能是实际问题,对吧? - Tieson T.
@Jan-Henk,这篇类似问题的答案是否引起了您的任何担忧:https://dev59.com/J3VC5IYBdhLWcg3wZwTj#306601 - Tieson T.
@TiesonT。我已经自己找到了那个信息,但是机器名称中没有“-”或“_”。不过还是谢谢你。 - Jan-Henk
@TiesonT。我会询问用户的时区。 - Jan-Henk
显示剩余7条评论

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.)


我将尝试在测试场景中传递会话ID,并在用户反馈问题后立即告知您结果。但是如果我没记错的话,--enable-trans-sid选项已被禁用,因为Google会使用不同的PHPSESSID值索引相同的页面多次。 - Jan-Henk
@Jan-Henk这很令人惊讶。我猜测Google爬虫机器人启用了cookie,因此会阻止PHP在URLS中使用SID。 - nl-x
这个更改是大约5年前进行的,所以现在可能已经不同了。但是直到最近一个月左右,我们从来没有遇到过会话数据方面的任何问题。 - Jan-Henk
代码已经有5年了,但问题是上个月开始的?这值得一提 :) ... 你是否升级了任何东西(Apache、PHP、防火墙、代理)?这是特定的IE版本问题吗?(也许只有使用最新IE的用户?) - nl-x
@Jan-Henk,还请看一下我的编辑。甚至可以将 $_SERVER['HTTP_HOST'] 记录到您的文本文件中。 - nl-x
显示剩余4条评论

-1
首先,您应该验证您的php.ini会话配置,特别是cookie持续时间。请在您的问题中添加此部分。 在出现错误的客户端上安装Fiddler,并生成完整的会话http转储。这将有助于您轻松跟踪问题。

Cookie的持续时间/配置已在注释中处理。IE F12开发人员工具可以完成。不需要使用Fiddler。 - nl-x
相关信息应该在问题中而不是注释中。顺便说一下,我在注释中没有看到php.ini部分。IE开发者工具无法保存完整的请求日志。 - Ghigo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接