我的平台上有一个有效的会话管理,我用PHP编程。
出于安全原因,我想至少在用户成功登录时重新生成会话ID,如果可能的话,我希望重新生成会话ID,以防止会话固定。
如果登录凭据正确,则登录部分如下所示。如果它们不正确或者用户还没有输入任何内容,我就不会开始会话,据我所知,会话ID的一个旧值被使用
session_set_cookie_params(1800, "/", $domain, true, true);
session_start();
session_regenerate_id(true);
每次我向服务器发出另一个请求,并检查我的会话是否有效,我打电话
session_regenerate_id(true);
我使用以下代码检查有效会话:
$domain = $_SERVER["HTTP_HOST"];
if(session_status() != PHP_SESSION_ACTIVE)
{
session_set_cookie_params(0, "/", $domain, true, true);
session_start();
}
//Check if session has expired
$now = time();
if(isset($_SESSION["discard_after"]) && $now > $_SESSION["discard_after"])
{
session_unset();
session_destroy();
header("Location: https://".$domain."/login.php");
die();
}
session_regenerate_id(true);
$_SESSION["discard_after"] = $now + 120;
if ((!isset($_SESSION["loggedin"]) || !$_SESSION["loggedin"]) &&
basename($_SERVER["PHP_SELF"]) != "login.php")
{
header("Location: https://".$domain."/login.php");
die();
}
我用Burp对此进行了监控并确认,当我成功登录时,会话ID会发生变化。当我浏览网站时没有任何问题和我想要的方式,会话ID也会发生变化。
然而,这种行为并不合适。我有一些用户输入正确的凭据(我知道这是因为日志),但没有有效的会话,也无法通过登录页面到主页。
我也有一个奇怪的例子,即必须登录两次(两次正确的凭证)才能设置有效的会话。这种情况对我来说不可预测以及对其他用户来说都是如此。
时,我还没有看到发生这种情况的时间,以及何时发生的情况有关设置的一些信息。我有一个带Apache2和PHP 5.X的Ubuntu 14.04 vServer。在客户端,我在Windows 7 64x上使用最新版本的Firefox和Chrome。即使凭据正确且上面的代码确定无法执行也无法登录的用户也在Windows 7 64x上使用Chrome(我没有关于此的更多信息)。
我确定问题是session_regenerate_id(true);
函数,因为如果我评论这一行,一切正常,但这个解决方案对我来说并不令人满意。我也试过一台PC,我当然从未进入过该网站,所有内容也都有效。
我无法看到导致这种情况的原因,PHP手册页并没有真正帮助我。特别是不确定的行为使我感到困惑。
直到明天我才回答,但我会重视所有有用的答案和评论。
修改
这是整个登录序列,没有不必要的东西,在我的场景中没有执行或绝对不重要。我从不输出任何东西。
if($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["inputEmail"],
$_POST["inputPassword"]) && $_POST["inputEmail"] !== "" &&
$_POST["inputPassword"] !== "")
{
$domain = $_SERVER["HTTP_HOST"];
$passwordHash = hash("sha256", $_POST["inputPassword"]."somesecretsalt");
//Own function which definitly works
$connection = connectToDB("standard");
//Get usernames and passwordhashes for checking
$query_getUser = "SELECT id, firstname, lastname, email,
passwordhash FROM user WHERE email = ?;";
if($stmt = mysqli_prepare($connection, $query_getUser))
{
mysqli_stmt_bind_param($stmt, "s", $_POST["inputEmail"]);
mysqli_stmt_execute($stmt);
$userRAW = mysqli_stmt_get_result($stmt);
mysqli_stmt_close($stmt);
}
if(mysqli_num_rows($userRAW) == 1)
{
//State: User exists
$dataset = mysqli_fetch_array($userRAW, MYSQLI_ASSOC);
$now = time();
//This is the crucial part which is definitly executed because the
// log entry in the following if clause is executed
session_set_cookie_params(1800, "/", $domain, true, true);
session_start();
session_regenerate_id(true);
if($dataset["passwordhash"] === $passwordHash)
{
//State: User exists, correct password
$_SESSION["loggedin"] = true;
$_SESSION["id"] = $dataset["id"];
$_SESSION["email"] = $dataset["email"];
$_SESSION["name"] = $dataset["firstname"]." ".$dataset["lastname"];
$comment = $_SESSION["name"]." (ID:".$dataset["id"].") has logged in";
writeToLog("Login success ", $comment, __LINE__);
if ($_SERVER["SERVER_PROTOCOL"] == "HTTP/1.1")
{
if(php_sapi_name() == "cgi")
{
header("Status: 303 See Other");
}
else
{
header("HTTP/1.1 303 See Other");
}
}
header("Location: https://".$domain."/index.php");
mysqli_close($connection);
die();
}
}
mysqli_close($connection);
}