I am a PHP novice and am trying to wrap my head around how sessions work. I had a general question about the behavior of sessions in php.
So I understand that every time session_start() is called the garbage collector may be invoked with a certain probability. So my first question is, lets say I only have 100 users of my website, and there are 2000 sessions stored in the session.save_path() folder on the web-server. If user A who is one of the 100 users logs in to the website and session_start() is invoked, are one of the previous sessions of user A destroyed or one of the 2000 sessions that belong to all the users destroyed?
My second question is, lets say user A is on the website for about 30 minutes and 'session.gc_maxlifetime' is set to 1440 seconds which is 24 minutes, after 24 minutes is there a chance the user A's session might be deleted by the garbage collector even though the user is still active on the website?
And if so is a new session with all the same information of the previously deleted session started every 24 minutes since this would have to be the case for prevention of loss of user data.
I apologize if these questions sound very rudimentary but I am quite confused about the session concept and would like the concept clarified.
Any help would be much appreciated.
PHP's session garbage collector runs with a probability defined by session.gc_probability divided by session.gc_divisor. By default this is 1/100, which means that above timeout value is checked with a probability of 1 in 100.
This means whenever a new session is started, there is a chance that garbage collector is triggered. Than all sessions that are older then the maxlifetime will be deleted
If someone is active on the site this session will never deleted. On every page refresh the actual session will be reset the lifetime of this session. Only not used sessions will be deleted. So if a user is just idling on the page and do nothing, this session could be deleted. And he have to start a new session.
I hope this helps you understand session
Garbage collection applies to ALL sessions, because PHP has no knowledge of session "ownership"; all session objects that are modified before time() - ini_get('session.gc_maxlifetime') will be removed.
Whenever a particular session object is accessed using session_start(), at the end of the request (implicit) or when session_write_close() is called (explicit), its modification time (should) get updated. This means that as long as a user keeps loading pages occasionally, the session is not destroyed.
Btw, I talk about session objects rather than files because the physical storage of sessions can be changed using session_set_save_handler().
Related
I have read several articles on this topic but still don't fully understand the relationship between the client-side session cookie, the server-side session file, and the way PHP "randomly" chooses to remove or keep a session during garbage collection.
The behavior I am trying to ensure is:
a user logs in, and the session begins
if the user is inactive* for a period of 1 hour, the session is
destroyed
*this is critical for my site because the user may be spending an hour apparently "idle" on the same page, but in fact they are not idle. They may be composing a long written report, watching a video, etc.
What I'm using so far is:
session_start();
setcookie(session_name(),session_id(),time()+3600);
This code is executed on every page load.
I also have an AJAX request firing every minute via a setInterval, which loads a PHP script containing the above code on pages where the user is apparently "idle".
Will my approach ensure the behavior I require? Or am I missing something? Perhaps there is a cleaner way to ensure this behavior.
Many thanks in advance.
you could try something like this:
if (isset($_SESSION['LAST_ACTIVITY'])&&(time() - $_SESSION['LAST_ACTIVITY'] > 3600)) {
// this takes 60 minutes
session_unset();
session_destroy(); // destroy session data that is in storage
}
I ... still don't fully understand the relationship between the client-side session cookie, the server-side session file, and the way PHP "randomly" chooses to remove or keep a session during garbage collection.
The client-side cookie does not hold any info about the user. It's only a token, which is sent to the server with any other info with every new request. The expiration limit you are setting indicates how long the client-side cookie is valid.
Server-side session files and garbage collection is something else. In php.ini you may set session.gc_maxlifetime, defaulting to 1440, which indicates "the number of seconds after which data will be seen as 'garbage' and potentially cleaned up". And then there is session.gc_probability (defaulting to 1) and session.gc_divisor (defaulting to 100).
This means, that after 1440 seconds there is a 1/100 the garbage collector will start, cleaning the session, which now is considered garbage.
If you wish to remove the session after an hour with a 100% probability, set session.gc_probability to 0 and remove the old sessions manually. For this, you could save "last activity" in your database and cron a script, which removes sessions, which have "last activity" and time() difference larger, than 1 hour.
My questions:
session.gc_maxlifetime in php.ini: Does the session.gc_maxlifetime start from the session_start() point or the latest request to the server? (Assuming I have a few requests without a session_start() being called.)
What is the best practice to use the $_SESSION object so as to not waste precious RAM (automatically clear idle sessions in time)? Or is this something that happens automatically by the time mentioned in session.gc_maxlifetime?
How do I correctly check if a session has expired (as against a session which never got created)? Or are both the same? isset($_SESSION['any_variable']) === FALSE
Assuming I don't have control over php.ini, how do I increase session.gc_maxlifetime?
session_start(): If a session has "timed out", calling session_start will always start a session with the previous variables unavailable(a brand new session). Is that correct?
Good question! I would assume that the default filesystem session handler would go off last access but not all filesystems support an atime timestamp. I'll see what I can find out on that front.
Sessions are by default stored as files on disc. They only take memory when loaded. Unless you've built a custom session handler that stores sessions in a RAM disc or in a memcache server or similar, or unless you're storing a huge amount of state in the user's session I doubt memory use will be a major concern.
When session_start() is called the previous session data is loaded into PHP. If the session has expired then there will be no session data to load and a new empty session will be created. So yeah, if you check for the existence of a variable in $_SESSION that you're expecting to always be there then you can use that to determine if the user's session has expired (but only after session_start() was called).
Simply set gc_max_lifetime to how long you want sessions to last in seconds. 600 is 10 minutes, 86400 is one day, etc.
Yes (with some caveats, see below).
There are a few things you need to be aware of with sessions though. First is that there's two components to a session: A server side state record that holds all the data stored in the session, and a client side token that PHP uses to associate a particular user with a particular state record. Normally the client side token is a cookie. Cookies have their own expiration date, so it's possible that the session can expire before the session state is due to do so. In that case the user will stop sending the token and the session state is effectively lost. If you're adjusting how long a session lasts you need to set both the server side state expiration time and the client side cookie expiration time.
As for stale state, the session garbage collection system doesn't always run every time session_start() is called. If it was the overhead would be crippling to a big PHP site with a lot of sessions. There are configuration options that specify the probability that the GC will run on any given invocation of session_start (I believes it defaults to 1%). If it doesn't run then a stale session record may still be treated as valid and used to populate $_SESSION. It probably won't have a serious effect on your system but it's something you need to bear in mind.
Basically, a php session won't expire while a user is surfing on a website. But "while the user is surfing on the website" means that there are get and post requests. Nevertheless, i can't figure out if there has to be new requests, or if one active request is enough to maintain the session…
For instantce, i have a big file upload by post. It could then take hours. Will the session expire or not ?
The lifetime of a session depends on the ini setting session.gc-maxlifetime. Every access to the session (read and write) resets the timer. After the timeout, when the session garbage collector runs, the session values are destroyed.
The default value is 1440, which means 24 minutes. So if you have hits that access the session in any way at least every 24 minutes, the session values will stay.
If you need the session to stay alive longer than that, you can extend the timeout with ini_set (use before session_start()), for example:
ini_set('session.gc_maxlifetime', 24*60*60); // 24 hours
It shouldn't. Usually when I work with $_SESSION's, they last for a day or so. But it might on some servers. In that case you need to add cookies, too. With cookies you can exactly manipulate the time the person can be online for.
I'm using Zend_Session to manage my user sessions, and I was looking to implement a "Remember Me" option in my application to keep users logged in for 2 weeks or so.
I've noticed that Zend_Session already has a built-in function called Zend_Session::rememberMe, however I'm not sure if that function logic is correct to use as a persisted login.
Essentially, the rememberMe function just extend the active session expiration date, which means if the user use the remember me option, he'll stayed logged in for 2 weeks with an active session.
This brings up 2 major issues.
I'm storing the sessions on the database, which means all these inactive users are stored for 2 weeks in my session table. I have over 50k inactive sessions, and it's hurting the application performance.
I want to know if a user came back to the site after 24 hours of inactivity, and revalidate his information. As his session remains open, I can't really tell if he came back after 1 hour or 1 week, as he has the same active session id.
I've read that if I want to implement a remember me feature, I shouldn't use the session cookie for that, and I should create another "login cookie" to remember a hashed user_id and a token. here's the complete explanation: What is the best way to implement "remember me" for a website?
So why does zend framework offers such a function, if using it can create performance and security issues?
One can only speculate their reasons for offering the function, but I don't see any major reasons against having it either. Plenty of programming languages give you facilities to do something bad or write code that has unseen negative side effects.
Sure, there can be unforeseen implications if someone arbitrarily sets it to a very large value, but the thing to note is that session data is still subject to garbage collection based on session.gc_maxlifetime regardless of the rememberMe time set on a cookie. Calling Zend_Session::rememberMe() has no effect on garbage collection for that data.
Consider the following:
Bootstrap.php
protected function __initSession() {
ini_set('session.gc_maxlifetime', 45); // set session max lifetime to 45 seconds
ini_set('session.gc_divisor', 1); // 100% chance of running GC
Zend_Session::start();
}
IndexController.php
public function indexAction() {
$data = new Zend_Session_Namespace('data');
if (!isset($data->time)) {
// no active session - set cookie lifetime and set some data
Zend_Session::rememberMe(90*86400); // 90 days
$data->time = time();
echo "Setting time";
} else {
echo date('r', $data->time);
}
}
If you were to access IndexController, the first time you would see Setting time. Then if you were to wait over 45 seconds, you would see the time printed out and (in my case) on the next request the session is expired. The session data is deleted from the server and although I still have the previous cookie, it is no longer recognized by the server.
I would expect that if you were implementing the garbage collection callback in your session save handler, then you should still see old session data removed from your database depending on what your gc_maxlifetime is set to.
To speak to your 2 questions:
As for your first issue, I would question why having 50,000 inactive sessions is hurting performance. If the database is properly indexed on the session ID, it should be extremely fast to get the session data even if there were millions of sessions in the database. Have you hit a hardware limitation possibly? Selecting data from 50,000 records when done properly should have little overhead.
In regards to your second issue, I agree with Mike, you should store a session value indicating when the last visit was, this way, when you start the session you can check their last visit and see how long it has been since their last page view. Then based on your threshold you can determine if they are returning to your site after being inactive.
For security reasons, if you detect it has been so long since their last visit, this is a good time to re-call rememberMe(), as doing so will issue a new cookie and help prevent session hijacking and fixation.
+1 for noting the major flaw behind Zend's approach to the 'remember me' functionality. Some people don't understand there is a penalty to be had when they attempt to extend the session lifetime, regardless of the session handler being file or db based. Allowing stale sessions to persist beyond a reasonable time-frame is a weak solution and you are better off implementing a custom cookie solution outlined by the link you provided.
The direct answer to your question; who knows. Maybe they didn't consider the fact that many users opt for database session handling, and figured piling up stale session cookies on the filesystem had no direct impact on performance.
Also, if you wanted to track if a user came back and re-established a stale session, you could add a 'updated_at' column to your session tracking table. So then you would have two timestamp columns; created_at and updated_at, which would help you make this determination.
I have been having this problem for some time now, I dont exactly know that if this is the issue but I am pretty confident that it is, I have my remember me session set too expire after 1 week, but when I go to my site after a few hours of inactivity my remember me session is gone, i check my servers tmp dir and the session flat file is gone, what i think is happening is some PHP session garbage collector runs every now and then, but i dont want it to delete these sessions that are suppoes to be stored for a week, how do a modify this behavior?
You're confusing two things.
A "remember me" mechanism doesn't rely on sessions. It relies on a cookie that stores credentials which are used to start a session. In this case, you have to setup the cookie so that is last for one week. See this answer.
If you just want to extend the lifetime of sessions, you have to both extend the lifetime of the session cookie to one week and delay garbage collection. This is done changing session.gc_maxlifetime.