I have built a session library, and I am having a very random bug (I don't really know how to unit test this, so I just filled everything with log messages and waited till it happened again) that translates into a user being logged out, due to a session ID mismatch.
The flow of the application goes like this:
A request with a valid session ID is made
Session data is found for that session ID in the DB
The 'last activity' happens to be old, so it is regenerated and updated in the DB
The new session ID is sent in the response (as a cookie)
This works fine almost always, but sometimes the next request fails to match the session ID, because (this is my guess) it was sent after we updated the database (in the previous request, which was still running), but before the response with the new cookie came in.
Did I misunderstand the concept of regenerating a session ID? I'm regenerating session ID's only for security reasons, so someone that chose to be logged in for a year, still has his session ID changed from time to time.
One option would be to keep multiple session ids per user, but put expiry times on them - when it's time to regenerate a session id, add the new one, and put an expiry time on the old one equal to some reasonable period of time (a minute, perhaps). Keep accepting the old one in addition to the new one until the old one expires.
I assume you're using session_set_save_handler(), right..? If so, try doing the following:
session_regenerate_id($delete_old_session = true);
session_write_close();
Or even:
session_regenerate_id($delete_old_session = false);
session_write_close();
Calling session_write_close() should effectively save the new session data. You only have to pay attention when you call this (usually before privilege changes > redirects), since it ends the session.
End the current session and store
session data.
Session data is usually stored after
your script terminated without the
need to call session_write_close(),
but as session data is locked to
prevent concurrent writes only one
script may operate on a session at any
time.
Related
I have a webapp that uses persistent cookies to allow a user to stay logged it.
I am using the Improved Persistent Login Cookie method.
https://www.programering.com/a/MDO0MzMwATA.html
https://www.experts-exchange.com/questions/29006560/selector-validator-cookies.html
When a user is logging in through the LOGIN form and has asked to be remembered I generate a random selector and a random token and add these to a table called Session in my DB along with the userID and other values(IP,time,browser,whaterver). I also set a cookie called KeepMeLoggedIn with the value selector:token and expire in 30 Days.
When the user returns to the site (before or after the PHP Session/Code Igniter has expired) I check for $_SESSION variable, if none found I look for my KeepMeLoggedIn cookie. If the cookie returns a value I check it against my Session table to see if the selector and token match. If they match I reset the token and store it back in the DB and cookie is updated to the new selector:token value and the login process completes.
When a user logs out I destroy the cookie and session and delete the entry in the DB for the selector.
All this is working great except for when a user deleted the cookies manually. The record in my Session table is orphaned. In testing my system I ended up with 50+ records in my Session table that were from the cookies I manually deleted while testing the logic. Since I manually deleted the cookie the selector was not available to the code to be deleted/removed from the Session DB.
So here is my questions:
1) What is a usable approach to handling these orphaned record?
My first thought is just purge the Session table of any date older then my chosen expiration date for the Remember Me function, either when a user logs in, or in a chron job, or whenever
Are there any other ideas here?
2) Is this a vulnerability in the overall model that can allow a hacker to:
create an account on a website
x=1
while x <2
-> login and ask to be remembered
-> delete the cookie
do();
And end up flooding the website's Session Table till the site is shut down, adding 1,000 and 1,000 of record over time??
I have read many answers on stackoverflow, but none of them could answer my question.
Let's consider a case, where a teacher and the student use the same computer. The teacher uses it to upload marks and the student use the computer to browse the marks. Now, the cookies of the teacher are stored and accessible. The student can easily forge the cookies and present himself as a teacher to the system and create havoc.
What approach should I take to disable this? Is this only possible via sessions? Or there exists a possibility with cookies as well.
The main two suggestions I would have are:
Delete the entire session right before the user logs out or logs in. A new session should always be started when authorization level changes.
Only accept sessions ids you generate. By default PHP will accept and start a new session for any value of the session id you send it. If you receive a session id you haven't seen before, discard it and send the user a new one.
If it's not necessary to have the browser remeber the cookie between browser sessions (eg a login can be 'forgotten' if the browser window is closed), then you could NOT set the expiry date on the cookie which makes it memory-resident only.
When the user closes the web broswer, the cookie is forgotten (standard browser behaviour) so there's no link to the old session, and a new login must be provided. The old session becomes "orphaned" and (assuming you have a 'expire through inactivity' process) will be expired eventually.
If you need the browser to remember the last user even if the window was closed, then a short-but-appropriate timeout is needed - destroy the session making the cookie useless after x minutes/hours inactivity (whatever is appropriate).
I want to add a "close other active sessions" button to my PHP web app.
I'm thinking of tracking (userid,sessionid,expiration) on a MySQL table and kill the sessions the following way:
$currentsessionid=session_id();
foreach($othersessions as $session){
sessionid($session['sessionid']);
session_start();
session_destroy();
}
mysql_query("DELETE FROM sessions WHERE userid = $currentuserid AND sessionid <> $currentsessionid");
session_id($currentsessionid);
session_start();
The table would also be updated after each session_start() if the user is logged, removing expired entries.
Expired entries are removed every time a user logs in.
Is this the proper approach? Any suggestions?
In PHP the sessions are normally isolated. You need to create your own session handler and write session (plus some extra tracking information) into the database so this become more easy to manage.
For example, like you have in your question, the userid. You then can search for sessions of a specific user.
Another field you should have is a timestamp so that you can let sessions expire, e.g. if older than X hours, you throw them away.
You can - if you like - also take a look if a user has got an existing session and import the old session. However this can have security implications. You need to think about what happens when an attacker logs on instead of the real user.
Can an attacker even throw the valid user out of the system then? That should be prevented.
Also take into consideration that you can take over the session data of a previous session under a new session ID. That is similar to session_regenerate_id(), a login should always regenerate the session id, as well as the logout and other, important steps (e.g. re-authentication before changing the email-address).
I have a situation where my system detects if cookie has been hijacked. If I clone the cookie between two different browsers, then my system can kill the session cookie on a browser that is the clone and thus protect the original session.
But I have a problem. How can I regenerate a new session ID value for the cloned browser in the same session? I would essentially give clone a new session ID that is clean of the original session ID data, without affecting the original session ID data.
This is what currently happens on my clone:
System detects that this session was started with a different fingerprint, thus it is a clone or hijacked cookie
Session data is made inaccessible, previous session data is written to disk with session_write_close() and after that the $_SESSION variable is cleared entirely.
User agent (browser) is notified to remove that cloned session cookie
What I want to happen though is that the cookie would not be deleted, but assigned with a new - different - session ID, so that instead of deleting the current session, a new one would be made that is empty.
I cannot use session_regenerate_id(), because that wants sessions to be started and it would start the previous session rather than a new one and my previous original session data would be lost and not accessible from the previous one. I can assign a new session ID with a session_id() function and then use that, but how can I generate the session ID value that is as secure as the one that PHP itself generates?
Basically all I want to do is that if I detect a cloned session, then I still want to start sessions, but I want to replace the session ID of the cloned session while keeping the original session still alive.
Alright, apparently it is doable with session_regenerate_id() method that accepts a variable that basically says whether it should keep previous session data or not. Using this I was able to code a workaround.
I need a php code that will run a query after a specific session is going to expire,
if(!isset($_SESSION['test'])) query;
something like that but does work with session expire.
The problem you'll run into: a session doesn't just expire by itself, a session is expired on the next page load if the user waited too long. As there's no communication between browser and server between requests (user action), the server won't know if e.g. the user just closed the browser (or is doing the dishes while the website remains open).
So I guess there's nothing wrong with your code as it'll tell you if the sessin is expired on the next pageload (as long as there is a next pageload :)).
If you really need to make sure, the query runs after a session expired, I guess you'll have to save your sessions to a database and run a "cleanup"-script on each pageload to run your query and get rid of expired sessions. (e.g. save "lastUserAction" and compare that to whatever your session-limit is)
I would suggest to save the date you created the session in the database and check if the session expires like:
if($db->checkExpiredSession('test') == true) {
$db->query('...');
}
Just a thought.