这些答案根本没有帮助我,但他们可能会帮助那些偶然发现这个问题的人:
我昨天整天都在打破我的花生,而且在登录我自己的应用程序时,我似乎无法幸免。关于SO的所有有用的答案都是"你开始会话了吗?" - 我有。
当我登录时,脚本返回状态正确并给我以下标题:
"Expires: Thu, 19 Nov 1981 08:52:00 GMT",
"Cache-Control: no-store, no-cache, must-revalidate",
"Pragma: no-cache",
"Set-Cookie: ADMSESSION=f27n49icvbv0bjfgh4bffojgs0; path=\/; secure; HttpOnly"
我可以验证此cookie确实存在于我的浏览器中,并且仅在浏览器关闭时才终止cookie。
现在这就是魔术不会发生的地方。从服务器接收到前进状态后,本地脚本会刷新页面以加载真正的后端会话。至少,这是它应该做的事情。该脚本重新加载页面,我再次受到相同的登录提示。它把我推到了墙上。
仔细检查后,我发现我的会话cookie根本没有被使用,所以我把它改成了一个不安全的cookie(Set-Cookie: ADMSESSION=f27n49icvbv0bjfgh4bffojgs0
)。此似乎适用于要求用户登录的所有页面...但是,它仅适用于这些页面,而不适用于登录页面。我无法绕过这个,因为它在逻辑中遵循相同的轨迹,并且在服务器完全完成处理之前不会发送任何标题。
以下是一些相关的片段,说明了逻辑流程以及设置和获取会话的方式。出于安全目的,已对某些信息进行了编辑。
<?php // index.php
/**
* just print everything, I want to know when, where, and how
* the brown sticky stuff starts hitting the ceiling whirly device.
*/
error_reporting(E_ALL);
ini_set("display_startup_errors", 1);
ini_set("display_errors", 1);
require_once "path/to/my/config.php";
// session and login manager
require_once "path/to/my/SecurityService.php";
use \Namespace\Of\My\SecurityService as LoginManager;
// argument false: don't log activity on this page.
$loginManager = new LoginManager(false);
// start session
$loginManager->secureSessionStart();
// @fixme: secureSessionCheck is not fired!?
if($loginManager->secureSessionCheck()) {
print file_get_contents("path/to/views/main.html");
} else {
print file_get_contents("path/to/views/login.html");
}
<?php // SecurityService.php:51-75 (inside login method called via POST)
if($this->verify($credentials["passwordHash"])) {
if($this->logger !== null)
$this->logger->write("info", "successful login from {$_SERVER["REMOTE_ADDR"]}.");
$_SESSION["browser"] = $_SERVER["HTTP_USER_AGENT"];
// redacted
// redacted
$_SESSION["visitor"] = $_SERVER["REMOTE_ADDR"];
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
session_commit();
if($this->logger !== null)
$this->logger->write("info", "User ({$_SESSION["email"]}) created in session (" . session_id() . ").");
return true;
}
<?php // SecurityService.php:92-127
/**
* secureSessionStart
* Starts a session in a secure way
*/
public function secureSessionStart() {
if(ini_set("session.use_only_cookies", 1) === FALSE) {
if($this->logger !== null)
$this->logger->write("error", "PHP ini is configured incorrectly. (session.use_only_cookies)");
return;
}
switch(session_status()) {
case PHP_SESSION_DISABLED:
if($this->logger !== null)
$this->logger->write("error", "Sessions are disabled. Unable to log this user in!");
return;
case PHP_SESSION_NONE:
session_set_cookie_params(0, "/", "", true, true);
session_name("ADMSESSION");
session_start();
break;
case PHP_SESSION_ACTIVE:
session_set_cookie_params(0, "/", "", true, true);
session_name("ADMSESSION");
session_start();
$oldID = session_id();
session_regenerate_id(true);
if($this->logger !== null)
$this->logger->write("info", "Session ({$oldID}) moved to (" . session_id() . ").");
break;
}
}
<?php // SecurityService.php:118-189
/**
* secureSessionCheck
* Checks the current session if it"s valid with current data
*
* @return boolean
*/
public function secureSessionCheck() {
if(!isset($_SESSION)) {
if($this->logger !== null)
$this->logger->write("error", "Session is not set.");
return false;
}
if(!isset($_SESSION[/* redacted */]) || !isset($_SESSION["browser"]) || !isset($_SESSION["visitor"])) {
if($this->logger !== null)
$this->logger->write("error", "Session (" . session_id() . ") does not contain a valid administrator.");
return false;
}
if($_SESSION["browser"] !== $_SERVER["HTTP_USER_AGENT"]) {
if($this->logger !== null)
$this->logger->write("warning", "Session (" . session_id() . ") browser conflicts with current user agent.");
return false;
}
if($_SESSION["visitor"] !== $_SERVER["REMOTE_ADDR"]) {
if($this->logger !== null)
$this->logger->write("warning", "Session (" . session_id() . ") visitor conflicts with current visitor IP.");
return false;
}
// redacted (gets $this->data)
if(!isset($this->data)) {
if($this->logger !== null)
$this->logger->write("error", "Data object is not set.");
return false;
}
if(!isset($this->data->/* redacted */) || !isset($this->data->/* redacted */) || !isset($this->data->/* redacted */) || !isset($this->data->/* redacted */)) {
if($this->logger !== null)
$this->logger->write("error", "Data object does not contain a valid administrator.");
return false;
}
$compare = // redacted
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
// redacted
$isValid = hash_equals($compare, $_SESSION[/* redacted */]);
if(!$isValid) {
if($this->logger !== null)
$this->logger->write("error", "Illegal session presented by {$_SERVER["REMOTE_ADDR"]}.");
return false;
}
return true;
}
我的日志片段(登录后一次)
2016-11-01 09:18:55 – [info] Session (359hk6v83pc3a82b9tlbn79ch5) moved to (6g05qcg7pocaht66ru5gdq8di6).
2016-11-01 09:18:55 – [info] successful login from 127.0.0.1.
2016-11-01 09:18:55 – [info] User (mail@cytodev.io) created in session (6g05qcg7pocaht66ru5gdq8di6).
2016-11-01 09:19:08 – [info] Session (6g05qcg7pocaht66ru5gdq8di6) moved to (jo0etnds61ip2ices59hrg1jl6).
最后一个片段中的奇怪之处在于,在将会话移动到新ID后,它不会记录任何内容。我希望(在我尝试访问未登录的页面的情况下)我的日志中至少包含以下内容:2016-11-01 10:08:47 – [error] Session (jo0etnds61ip2ices59hrg1jl6) does not contain a valid administrator.
- 登录页面上的奇怪行为......
问:您是否检查过浏览器控制台?那里有与HTTPS有关的任何警告?也许混合内容警告或类似的东西? - CBroe
A:无论如何。登录页面加载没有任何问题,将数据发送到服务器,服务器响应正确。无法找到与HTTPS相关的控制台消息。网络选项卡也不显示200以外的任何状态。登录一次(通过POST请求),然后页面刷新。我可以像这样跟踪会话cookie:1(初始加载) - &gt;没有发送,1收到(071s21bdejg40supml9kamced5)。 2(POST登录) - &gt;没有发送,1收到(813um7v1jjh3jsi57g5k8evm16) - 这不应该是一个问题,对吧?由于用户应该登录到新的会话ID。 3(重新加载) - &gt; 1发送(071s21bdejg40supml9kamced5),1收到(k5aa0o4k24v2cfc41qbleq04h4)......
通过检查发送/接收的cookie找到了实际问题。解决这个问题的方法没有。
根据我在阅读CBroe评论后所采取的步骤找到了这个问题。
我似乎从服务器回来了错误的会话cookie,我相信我可以将其归因于secureSessionStart
函数。我删除了cookie的设置(用于测试目的)并且我已正确登录。但是,这会破坏使用无法通过脚本访问的cookie的安全会话的整个目的。当我只使用session_start()
时,一切都很好,我受到应用程序实际后端的欢迎。每当我使用session_set_cookie_params(0, "/", "", true, true)
时,登录页面会一次又一次地弹出。
它可能在某处丢失了分号。它总是缺少分号......
答案 0 :(得分:1)
显然,我的服务器没有收到安全标头,这导致我的登录会话在重新加载时丢失。我目前通过在设置会话之前检查连接是否安全来解决此问题。
我不要建议使用不安全的连接来处理您的登录请求!如果你遇到了我一直遇到的同样的问题,请务必检查连接是否一直都是安全的,然后再在会话cookie中关闭它!
对secureSessionStart
方法进行了两处小的添加,以便为会话cookie添加域名和安全性。请始终确保连接 安全。这应仅用于测试目的!
/**
* get's the secure status from _SERVER
* Also check against HTTP_X_FORWARDED and HTTP_X_FORWARDED_SSL because
* some servers are behind load balancers.
*/
$secure = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on") && ((!empty($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] === "https") || (!empty($_SERVER["HTTP_X_FORWARDED_SSL"]) && $_SERVER["HTTP_X_FORWARDED_SSL"] === "on"));
// also use _SERVER["SERVER_NAME"] to set the cookie to this domain only
session_set_cookie_params(0, "/", $_SERVER["SERVER_NAME"], $secure, true);
理想的解决方法是在需要它的每个页面上使用SSL。我正在使用的当前域没有这个设置,我正在修复它,因为我正在输入它。我甚至不开玩笑。在构建登录系统之前获取SSL。