Preface
These answers did not help me at all, but they might help someone who stumbles upon this question:
- session variable empty after page reload
- php sessions are empty after reload page
- php session variables blank after page refresh
- php session variable shows as empty on refresh
- why php session is deleted on page reload
- why is my session array ok on one page but empty on another
- weird session behavior session dissappearing (closed by developer: unable to replicate issue)
- properly using session set cookie params (followed, but still no dice)
The question
I have been breaking my peanut over this all day yesterday and I can't seem to catch a lucky break when it comes to logging into my own application. All helpful answers on SO are along the lines of "Did you start a session?" - Which I have.
When I log in, the script returns with status correct and gives me the following headers:
"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"
I can verify that this cookie is indeed present in my browser, and the cookie is terminated only when the browser closes.
Now this is where the magic doesn't happen. After receiving the go ahead status from the server the local script refreshes the page in order to load the real back-end session. At least, that's what it's supposed to do. The script reloads the page, and I am greeted by the same login prompt again. It's driving me up the wall.
After careful examination I found that my session cookie was not being used at all, so I changed it over to an insecure cookie (Set-Cookie: ADMSESSION=f27n49icvbv0bjfgh4bffojgs0
). This seems to work on all pages that require the user to be logged in... However, it only works on those pages, and not on the login page. I cannot wrap my head around this, since it all follows the same trajectory in the logic and no headers are sent until the server is completely done with the processing.
Relevant snippets
Here are some relevant snippets that illustrate the logic-flow and way of setting and getting the session. Some information has been redacted for security purposes.
<?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;
}
Snippet of my log (after logging in once)
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).
The strange thing in the last snippet is that it does not log anything after moving the session to a new ID. I would expect (as is the case when I try to access pages without logging in) at least the following in my log: 2016-11-01 10:08:47 – [error] Session (jo0etnds61ip2ices59hrg1jl6) does not contain a valid administrator.
- Strange behavior on the login page...
Questions and answers
Q: Have you checked the browser console? Any warnings relating to HTTPS in there? Maybe a mixed content warning or something like that? – CBroe
A: None whatsoever. The login page loads without any issues, sends the data over to the server, and the server responds correctly. No console messages can be found relating to HTTPS. The network tab also does not show any status other than 200. Login is called once (via the POST request) and the page refreshes afterwards. I can trace the session cookie like so: 1 (initial load) -> none sent, 1 received (071s21bdejg40supml9kamced5). 2 (POST to login) -> none sent, 1 received (813um7v1jjh3jsi57g5k8evm16) - This shouldn't be an issue, right? Since the user should be logged in to the new session ID. 3 (reload) -> 1 sent (071s21bdejg40supml9kamced5), 1 received (k5aa0o4k24v2cfc41qbleq04h4)...
The actual issue has been found by checking the cookies sent/received. The fix to this issue has not.
Current understanding of the problem
The issue was found based the steps I took after reading CBroe's comment.
It seems that I was getting the wrong session cookie back from the server, and I believe that I can attribute that to the secureSessionStart
function. I removed setting of cookies (for testing purposes) and and I am logged in correctly. However, this defeats the entire purpose of using secure sessions with cookies that cannot be accessed by script. When I simply use session_start()
all is well and I am greeted by the actual back-end of the application. Whenever I use session_set_cookie_params(0, "/", "", true, true)
the login page pops up again and again.
Final thoughts
It's probably a missing semicolon somewhere. It's always a missing semicolon...