5

我对某些会话功能以及 Chrome 进行预取/渲染的方式有疑问。我正在尝试将一个论坛软件(esoTalk)与自定义 laravel 4.3 应用程序连接起来。我有身份验证事件侦听器导致 laravel 创建一个允许论坛和应用程序共享身份验证详细信息的 php 会话(除了内置的 laravel 会话)。在访问论坛时,如果用户未登录 - 但存在此共享信息(即用户已登录 laravel 应用程序),论坛将使用会话中可用的信息登录该用户。

在大多数情况下,这工作得很好,除了 Chrome 预取似乎会破坏一些东西。如果我使用调试器监控论坛,我可以看到,当我输入论坛 url 但在我点击 enter 之前,chrome 将访问论坛。通过调试器,我可以看到它完成了它需要做的所有事情并成功登录。作为最后一步,论坛重新生成会话 ID 以停止劫持。这就是它崩溃的地方。看起来 chrome 忽略了新的会话 ID(通过 http SetCookie 标头发送),因此当我按 Enter 键时,我会使用原始会话 ID 进入论坛(并发出一个全新的请求)。这个 id 不存在,所以我设置了一个新的,因此失去了我的登录状态。对于用户来说,这看起来就像他们从未登录过。

我在谷歌上搜索了很多关于如何解决这个问题的建议。我不愿意删除会话 ID 重新生成,因为它确实服务于安全目的。我也无法禁用 chrome 预取/渲染。总而言之,我似乎有点苦恼。

我创建了一些复制此代码的代码。虽然它依赖于预渲染(因此您需要通过地址栏多次点击每个文件)

// test1.php

<?php

function regenerateToken()
{
    session_regenerate_id(true);
    $_SESSION["token"] = substr(md5(uniqid(rand())), 0, 13);
    $_SESSION["userAgent"] = md5($_SERVER["HTTP_USER_AGENT"]);
}

// Start a session.
session_set_cookie_params(0, '/');
session_name("SessionBork_Test_session");
session_start();

$_SESSION["SentryUserId"] = '99';

regenerateToken();

header('Content-Type: text/plain');
foreach ($_SESSION as $k => $v) {
    echo $k . " = " . $v . "\n";
}

访问 test1.php,然后访问 test2.php,您应该会看到一堆会话变量输出。一旦预渲染/获取开始,您就会开始收到一条损坏的消息。

// test2.php
<?php

function regenerateToken()
{
    session_regenerate_id(true);
    $_SESSION["token"] = substr(md5(uniqid(rand())), 0, 13);
    $_SESSION["userAgent"] = md5($_SERVER["HTTP_USER_AGENT"]);
}

// Start a session.
session_set_cookie_params(0, '/');
session_name("SessionBork_Test_session");
session_start();

if (empty($_SESSION["token"])) regenerateToken();

// Complicate session highjacking - check the current user agent against the one that initiated the session.
if (md5($_SERVER["HTTP_USER_AGENT"]) != $_SESSION["userAgent"])
    session_destroy();

// Log in a the user based on the SentryUserId
// ... logging in, setting userId, regenerating session
$_SESSION["userId"] = '10';
regenerateToken();

header('Content-Type: text/plain');
foreach ($_SESSION as $k => $v) {
    echo $k . " = " . $v . "\n";
}
if ( ! isset($_SESSION['SentryUserId'])) echo "\n--\nPrerendering brokeded me.";

如果您可以将它连接到 IDE 中的 xdebug 或其他东西,您应该会看到隐藏的 prerender 命中 test2.php (在响应中看起来是绝对正确的),然后当您按下回车键时,随后的实际命中忘记了谁你是。

4

1 回答 1

1

解决此问题的一种方法是检测预取,而不是在这些负载上生成新的会话 ID。有关在各种浏览器中检测预取的信息,请参阅此 Stack Overflow:HTTP 标头检测 Google Chrome 的预加载请求

此外,我认为有更好的方法来防止会话劫持(例如,将会话绑定到 IP 地址、浏览器签名等)

此外,您的代码中可能存在第二个错误:调用session_destroy()会破坏会话并关闭用户的会话。您需要在调用session_start()之前先调用session_regenerate_id()请参阅此处的文档和此处的示例。

于 2015-05-03T12:45:34.203 回答