Reputation: 256
I have a question about using the global $_SESSION[]
variable.
As of right now for my web-application whenever a user logs in, a row that matches that username and that specific password is returned. I then get the column user_hash
and store that user_hash
in the $_SESSION['id']
.
I use this session to recognize which user has logged in and perform database queries based on this unique hash. I read somewhere that having a "static" identifier like this (the static identifier being the user_hash, as it never changes) is unsafe, and that I should rather have the user_hash change dynamically.
So I implemented a system where everytime a user logged and/or navigated to some other page within the web-application, I would generate a new hash, store it in the database, and then store it in the global $_SESSION['id']
variable. I managed to do this, but now everytime someone tries to log in from two devices, the device with the "older" session is invalid, because the user_hash changed from the new device and logs off the user with an older session.
My question is, is there really any point in doing this? I am using ssl (https) for my webserver, so session hijacking is relatively off the table I think? If there is a security flaw by only having a static session identifier, is there a better way to make it dynamic, while on the same time not logging the other user off?
Upvotes: 0
Views: 938
Reputation: 22760
Two Useful Links:
http://blog.teamtreehouse.com/how-to-create-bulletproof-sessions
https://paragonie.com/blog/2015/04/fast-track-safe-and-secure-php-sessions
I managed to do this, but now everytime someone tries to log in from two devices, the device with the "older" session is invalid, because the user_hash changed from the new device and logs off the user with an older session.
While this may be nessecary depending on your security requirements, the first link I posted above says this:
mark the old session as obsolete and mark it to expire in ten seconds [or some time that suits your needs]. This way any queued up requests will still have access to the expired session but we don’t have to leave it open forever.
static function regenerateSession()
{
// If this session is obsolete it means there already is a new id
if(isset($_SESSION['OBSOLETE']) || $_SESSION['OBSOLETE'] == true)
return;
// Set current session to expire in 10,30,100 seconds etc.
$_SESSION['OBSOLETE'] = true;
$_SESSION['EXPIRES'] = time() + 10; //or 30 seconds or whatever.
// Create new session without destroying the old one
session_regenerate_id(false);
// Grab current session ID and close both sessions to allow other scripts to use them
$newSession = session_id();
session_write_close();
// Set session ID to the new one, and start it back up again
session_id($newSession);
session_start();
// Now we unset the obsolete and expiration values for the session we want to keep
unset($_SESSION['OBSOLETE']);
unset($_SESSION['EXPIRES']);
}
So I implemented a system where everytime a user logged and/or navigated to some other page within the web-application, I would generate a new hash, store it in the database, and then store it in the global $_SESSION['id'] variable.
On a personal note I would suspect it is extremely in-efficient and will cause you a large overhead of server load this constant reinvention. It is just not needed.
If you really want to reinvent the session, simply use session_regenerate_id()
and set it to a time value (say every 10 minutes), as outlined in the first link, at the top of this post.
The only value that goes over the wire, that matters, is the session identification all values within the session array are never seen by the browser or the end user.
So; as long as your session id is a LONG randomised value, it hides in plain sight. From the second link above for more destails about how to ensure PHP gives you much more random and longer named session ids.
php.ini:
session.save_handler = files
session.use_cookies = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_domain = "example.com"
session.cookie_httponly = 1
session.entropy_length = 32
session.entropy_file = /dev/urandom
session.hash_function = sha256
session.hash_bits_per_character = 5
If storing sessions in a database please do ensure that you make your column names for the session id long enough, else sessions will never connect due to truncation of the session name.
Some other factors:
Do ensure that your session cookies are set as encrypted
by PHP. This has to be manually done regardless of the state of your TLS connection. see session.cookie_secure=1
, above.
You can help minimise the already unlikely event of hjacking by adding in browser or ip specific data (such as a hash of these respective $_SERVER
values) to compare when carrying out tasks that warrent double checking the session, to compare that the browser type/client IP still compares to what's in the session record. There can be issues with each of these two approaches, such as a minority of people on mobile connections may change IP during login or users on multiple devices probably will have different browsers on each, but find a methodology to compare as you need (This is also referenced in the first link at the top of the page).
As stated above, the $_SESSION
data is never stored on the client machine, so hashing values from the Database to store in the $_SESSION
is very probably above and beyond your security needs.
The (single) most important thing is that the session id is secured and large enough to comfortably accommodate a potential hacker fishing with fake session ids as a kin to playing battleships on minecraft
Upvotes: 1