'Properly regenerating session ID's
I was looking at https://www.php.net/manual/en/function.session-regenerate-id.php because I wanted to learn how to properly deal with sessions and so I have used this example:
<?php
// NOTE: This code is not fully working code, but an example!
// my_session_start() and my_session_regenerate_id() avoid lost sessions by
// unstable network. In addition, this code may prevent exploiting stolen
// session by attackers.
function my_session_start() {
session_start();
if (isset($_SESSION['destroyed'])) {
if ($_SESSION['destroyed'] < time()-300) {
// Should not happen usually. This could be attack or due to unstable network.
// Remove all authentication status of this users session.
remove_all_authentication_flag_from_active_sessions($_SESSION['userid']);
throw(new DestroyedSessionAccessException);
}
if (isset($_SESSION['new_session_id'])) {
// Not fully expired yet. Could be lost cookie by unstable network.
// Try again to set proper session ID cookie.
// NOTE: Do not try to set session ID again if you would like to remove
// authentication flag.
session_commit();
session_id($_SESSION['new_session_id']);
// New session ID should exist
session_start();
return;
}
}
}
function my_session_regenerate_id() {
// New session ID is required to set proper session ID
// when session ID is not set due to unstable network.
$new_session_id = session_create_id();
$_SESSION['new_session_id'] = $new_session_id;
// Set destroy timestamp
$_SESSION['destroyed'] = time();
// Write and close current session;
session_commit();
// Start session with new session ID
session_id($new_session_id);
ini_set('session.use_strict_mode', 0);
session_start();
ini_set('session.use_strict_mode', 1);
// New session does not need them
unset($_SESSION['destroyed']);
unset($_SESSION['new_session_id']);
}
?>
I have tried to play with the example a bit and adding it but I have noticed one problem, whenever I log in the session gets wiped again and again, why?
The final look of the code is as follows:
<?php
// NOTE: This code is not fully working code, but an example!
// my_session_start() and my_session_regenerate_id() avoid lost sessions by
// unstable network. In addition, this code may prevent exploiting stolen
// session by attackers.
function my_session_start() {
session_start();
if (isset($_SESSION['destroyed'])) {
if ($_SESSION['destroyed'] < time()-300) {
// Should not happen usually. This could be attack or due to unstable network.
// Remove all authentication status of this users session.
remove_all_authentication_flag_from_active_sessions($_SESSION['ID']);
throw(new DestroyedSessionAccessException);
}
if (isset($_SESSION['new_session_id'])) {
// Not fully expired yet. Could be lost cookie by unstable network.
// Try again to set proper session ID cookie.
// NOTE: Do not try to set session ID again if you would like to remove
// authentication flag.
session_commit();
session_id($_SESSION['new_session_id']);
// New session ID should exist
session_start();
return;
}
}
}
function my_session_regenerate_id() {
// New session ID is required to set proper session ID
// when session ID is not set due to unstable network.
$new_session_id = session_create_id();
$_SESSION['new_session_id'] = $new_session_id;
// Set destroy timestamp
$_SESSION['destroyed'] = time();
// Write and close current session;
session_commit();
// Start session with new session ID
session_id($new_session_id);
ini_set('session.use_strict_mode', 0);
session_start();
ini_set('session.use_strict_mode', 1);
// New session does not need them
unset($_SESSION['destroyed']);
unset($_SESSION['new_session_id']);
}
my_session_start();
my_session_regenerate_id();
if(empty($_SESSION['ID'])) header("Location: /login/");
?>
Please, any details you need, ask me.
Solution 1:[1]
The issue with the example code in the php manual for session_regenerate_id() is the new session_id isn't in the data store yet, so when session_start() is called, there are no session variables to load.
I take care of this in the code below by setting a variable
$save_session_vars = $_SESSION;
to store the session vars and then upon starting the new session do the reverse:
$_SESSION = $save_session_vars;
You could use session_encode() and session_decode to do same thing instead.
Also worth noting in the regenerate_id sample code is
ini_set('session.use_strict_mode', 0); session_start(); ini_set('session.use_strict_mode', 1);
The second ini_set will fail because a session is started. I do not believe this is even necessary as the strict_mode setting causes php to check if the session is on file when a user supplied session_id is used to start a session (exactly what we are doing and why strict_mode has been turned off). Once the session is started there is probably no harm in leaving strict_mode turned off. However, in the function below, I close the session again so that I can tag an initial access to the new session and as a regenerated session, so I go ahead and reset the strict_mode to 1 while the session is closed.
It's may be obvious, but I'll note that if for some reason you have more than one session_start() in your app, you need to replace all with the regen_session_start() so the destroyed flag gets checked.
<?php
function regenerate_session_id_unstable_networks() {
// get a new session_id, then set this value on
// current session + timestamp in case it is
// accessed soon because of unstable network not
// getting new cookie value from our upcoming change.
// this value is checked in regen_session_start()
// --------------------------------------------------
$new_session_id = _sess_create_sid();
$_SESSION['new_session_id'] = $new_session_id;
$_SESSION['destroyed'] = time();
//
// Write and close current session;
// ---------------------------------
session_write_close();
// We have to turn off strict mode here
// because new session_id won't be on file
// and we'll will get another session_id
// from session_start()
// --------------------------------------
$was_in_strict_mode = false;
if (1 == ini_get('session.use_strict_mode')) {
ini_set('session.use_strict_mode', 0);
$was_in_strict_mode = true;
}
// save our current session vars as they are
// still set even though we closed the session
// --------------------------------------------
$save_session_vars = $_SESSION;
// Start session with new session ID. By setting session_id
// to our new session id we get a new empty session because
// it's not stored yet, the same point as with ini setting.
// --------------------------------------------------------
$strict_mode = ini_get('session.use_strict_mode');
session_id($new_session_id);
regen_session_start();
$cur_session_id = session_id();
//
// Restore our saved session vars
// ------------------------------
$_SESSION = $save_session_vars;
//
// and then remove the breadcrumbs left on old session from new session
// --------------------------------------------------------------------
unset($_SESSION['destroyed']);
unset($_SESSION['new_session_id']);
//
// close session. reset strict mode and start session again
// Note: must change ini settings while session is closed
// --------------------------------------------------------
session_write_close();
if ($was_in_strict_mode) {
@ini_set('session.use_strict_mode', 1);
}
// mark_new_session_as_regenerated_for_forensics();
regen_session_start();
}
// -------------------------------
//
// -------------------------------
function regen_session_start() {
$result = session_start();
// if started session has been flagged destroyed,
// either take evasive action or activate the new session
// ------------------------------------------------------
if (isset($_SESSION['destroyed'])) {
// see if this access is after 5 minutes of change to session id
// if so it's suspect so do something about it.
// ---------------------------------------------------------------
if ($_SESSION['destroyed'] + 300 < time()) {
remove_all_authentication_flag_from_active_sessions($_SESSION['userid']);
do_some_logging_and_take_some_action();
throw(new DestroyedSessionAccessException);
}
// Hasn't been 5 minutes yet so not fully expired.
// Could be because lost cookie by unstable network
// (new session cookie value didn't get set on client).
// Try again to set proper session ID cookie by closing session,
// setting new session id, and calling session_start.
// Sort of defeating purpose of key change so some security
// risk in doing this for too long or at all, adjust to meet needs.
// ----------------------------------------------------------------
if (isset($_SESSION['new_session_id'])) {
//increment_destroyed_access(session_id());
session_commit();
session_id($_SESSION['new_session_id']);
// New session ID should exist
session_start();
}
}
return $result;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | just-a-coder |
