UPDATED
So I am running into another problem that I think is pretty much the same thing. I am using silex and I want the session to be stored for 5 days. I give the have the following values:
session.cookie_lifetime: 432000
session.gc_maxlifetime: 432000
session.gc_probability: 100
session.gc_divisor: 100
So from my understanding since probability and divisor are the same number, gc should happen every page load and the session file (using php native file handler for sessions) should be kept around for 432000 (60 * 60 * 24 * 5) seconds without any activity on them. This issue is even with these value, my session seems to expire between 1-2 hours of no activity.
I even tried seeting the gc_maxlifetime to 5 and that seemed to work fine so it makes me think something else is being triggered before gc_maxlifetime is.
Are there any other values I am missing?
The server decides when to clear up stale sessions.
This happens at certain times, depending on a random number and the chance you gave it (in php.ini).
Look for session.gc_divisor.
So in your situation, your cookie with the PHPSESSID is still there, and your browser sends it as it should to the server, but the server cannot find a corresponding session.
You also need to change a couple more variables in your php.ini
.check this link
Related
gc_maxlifetime is already set to 24 minutes, but when each application has a different sess_expiration. One of them is set to 9000 seconds (2 1/2 hours). The app is expired based on gc_maxlifetime not sess_expiration. Why is that? How can the sess_expiration work if it is longer than gc_maxlifetime?
Reading from here: why ini_set('session.gc_maxlifetime',60) doesn't work? and here: https://www.dev-metal.com/how-the-php-session-garbage-collector-really-works/
Because garbage collector starts (if starts) before session
I think that the gc_maxlifetime fires before your CI session handler and for this it 'wins'.
For not being forced to modify your php.ini file, you could try to set it before each session_start:
ini_set("session.cookie_lifetime","7200");
ini_set("session.gc_maxlifetime","7200");
session_start();
Or in your .htaccess file:
php_value session.gc_maxlifetime 7200
php_value session.cookie_lifetime 7200
You could read more here: Codeigniter increase session time out not working
Hope it helps!
Not really as simple as you've put it ...
It is true that gc_maxlifetime is what determines if a session should be deleted or not, because that's effectively the "server-side timer" that counts towards the deletion of a session - there's one on the client side as well, because that's where cookies are stored.
However, CodeIgniter will set gc_maxlifetime to the same value that you put in sess_expiration, unless it is 0 (in which case it uses your php.ini value).
But something else in your question may be important:
but when each application has a different sess_expiration. One of them is set to 9000 seconds (2 1/2 hours)
If you are using the same sess_save_path, sess_cookie_name, sess_match_ip on the same server, but for multiple applications ... then the application with the lowest sess_expiration value will at some point delete sessions that you intended to be still valid for others.
TL;DR: Don't use the same session "space" for separate applications.
The scenario:
User logs in
Cookie is set to length of session
After 1 hour of inactivity I wish to log out the user
How I think I can solve this:
Set the session.gc_maxlifetime to 1 hour (3600)
Set the session.gc_probability to 1
Set the session.gc_divisor to 1
Therefore having a 100% certainty that garbage collection will occur on any idle session cookies after 1 hour.
My question:
All the posts and documentation I've read has never mentioned setting a gc change of 100%, therefore is it bad to do this? Is there a better way?
It's a symfony app, and long term I would like to do something like this http://symfony.com/doc/master/components/http_foundation/session_configuration.html#session-meta-data but for now I was hoping to just do something simple with session.gc_*
One post I read implies that having a 100% garbage collection chance is "cost-intensive" How do I expire a PHP session after 30 minutes? is this true? If so, how cost intensive?
Cheers!
The gc_probability and gc_divisor are there to let you define the "probability" of firing up the garbage collection (GC).
Since GC (as everything) comes with a cost, you wouldn't usually want it to run on each and every web request processed by your server - that would mean that every page opening or every AJAX request served from PHP would cause the GC to run.
So, depending on the actual server load and usage, the admin is expected to do an educated guess on how often should GC be run: once in 100, 1/10000 or 1 in million requests.
But, there's a problematic flaw in the OP's original reasoning - that garbage collection will occur on any idle session. The way I read the manual, the garbage collection will occur on ANY session, not just idle ones:
session.gc_maxlifetime integer: specifies the number of seconds after which data will be seen as 'garbage' and potentially cleaned up.
So, the session (idle or not) lifetime is decided with gc_maxlifetime, while the moment of the GC being started (as said in the docs: "potentially") is really decided with gc_probability and gc_divisor.
To resume, my late answer to the question would be - I would not under normal condition have GC running at each and every request (the 1/1 scenario you mentioned), because
that seems like a serious overkill. On some level, you would probably end up with thousands (if not worse) of IFs and only once going into its THEN
you would log out ANY user on your system after 60mins, not just the idle ones.
There are much better ways of doing this.
If this isn't for something particularly secure, you can set an expiration date/length for the session cookies on the client-side. A technically minded user could tweak the expiration in this case, so you wouldn't want to use this on a bank site.
If you need something more secure, just store an expiration time along with the other session data and check against it. If it's exceeded, destroy their session and force them to log back in.
From the PHP manual, session.gc_probability and session.gc_divisor state that gc will occur based on this probability. I get that.
What I'm not clear on is whether this probability is on a session by session basis or overall.
So if my probability is 1% (1/100) that GC will occur, does that mean that if one session keeps getting extended, each time there is a 1% change that specific session will be cleaned up? Or does this mean that 1% of all existing sessions (as well as new ones) will trigger GC for all other existing sessions?
I'm pretty sure it's the latter, I just want to make sure.
The purpose of this question is that on our site, I want users to have long-term sessions (6 months). If 1% of all sessions trigger GC, then that effectively removes the purpose of having that long-term session, as GC will end up occurring every hour or two.
Every time a PHP script is executes and starts session there is a probability that it will sweep through the session folder killing off old session.
Cleanup will only delete sessions which were not accessed within a certain time. However PHP does not guarantee that the session WILL be destroyed within that time.
Your long-term session strategy should work just fine, but you might want to reduce 1% to something like 0.1%
Another thing to look out for is that operating system might clean up your /tmp folder during reboot so even if PHP won't do it.
last time I looked at the source each call to session_start() "rolled the dice" so to speak, using the divisor and probability. If you hit, then it would delete all files from the session.save_path directory that were older than session.gc_maxlifetime. I forget if it used modification or access time of the file, although it shouldn't matter in normal curcumstances because php overwrites the session file by default at the end of script execution, so mod and access times should almost always match very closely.
// Rough psuedo code of how php's session_start() function works regarding garbage collection.
function session_start() {
$percentChanceToGC = 100 * ini_get('session.gc_probability') / ini_get('session.session.gc_divisor');
$shouldDoGarbageCollection = rand(1, 100) < $percentChanceToGC;
if ($shouldDoGarbageCollection) {
$expiredCutoffTime = time() - ini_get('session.gc_maxlifetime');
foreach (scandir(ini_get('session.save_path')) as $sessionFile) {
if (filemtime($sessionFile) < $expiredCutoffTime) {
unlink($sessionFile);
}
}
}
// ... rest of code ....
}
I don't know how many session files you're going to end up having hang around if you want them to live for a minimum of 6 months. Consider it may take a little while for php to stat many thousands of files to determine their age. Maybe consider other options for durable storage of this data. Or you could disable php gc and just run a cron job to delete stale session files. Otherwise, that 1% of requests are gonna trigger gc and have to wait for php; in other words it could possibly lag.
I'm not an expert on this, but from reading the manual, I'd draw your attention to another setting, session.gc_maxlifetime. From the docs:
session.gc_maxlifetime specifies the number of seconds after which data will be seen as 'garbage' and potentially cleaned up. Garbage collection may occur during session start (depending on session.gc_probability and session.gc_divisor).
So if you set this setting to a suitable value (60 * 60 * 24 * 365 / 2 for half a year, so 15768000), then the appropriate data won't be eligible for garbage collection, no matter what the other settings are.
I have implemented memcache in my PHP-MySQL based app and it gets updated regularly from a backend process.
Due to this some data is conflicting with the expiration time and other backend processes, so I came up with a solution but for that I would have to make the TTL = infinite.
Easy - just write 0 there.
expire
Expiration time of the item. If it's
equal to zero, the item will never
expire. You can also use Unix
timestamp or a number of seconds
starting from current time, but in the
latter case the number of seconds may
not exceed 2592000 (30 days).
You can set the TTL to 0, which means that it should 'never' expire.
But remember that it will never really be infinite. The data is stored in memory and will be lost under some circumstances, the most obvious being the server being rebooted. :)
You should always have the possibility to reconstruct that data when the memcache fails.
More details to be found here.
As far as I know, if you don't set a ttl, it will never expire.
However, there are replacing policies for keys, about which you can read here
What is the actual difference between session.gc_maxlifetime and session_cache_expire() ?
Suppose I want the users session to be invalid after 15 minutes of non-activity (and not 15 after it was first opened). Which one of these will help me there?
I also know I can do session_set_cookie_params() which can set the user's cookie to expire in some amount of time. However, the cookie expiring and the actual session expiring on the server side are not the same; does this also delete the session when the cookie has expired?
Another solution I have though of is simple
$_SESSION['last_time'] = time()
on every request, and comparing the session to the current time, deleting the session based on that. I was hoping there was a more "built-in" mechanism for handling this though.
Thanks.
I spent some time looking for a good answer to how the php.ini server settings make
sessions expire. I found a lot of info but it took a while to figure out why
the settings work the way they do. If you're like me, this might be helpful to you:
Sessions are stored as cookies (files on the client's pc) or server side as files
on the server. Both methods have advantages and disadvantages.
For the sessions stored on the server, three variables are used.
session.gc_probability
session.gc_divisor
session.gc_maxlifetime
(session.gc_probability/session.gc_divisor) produces the probability that the
garbage collection routine will run. When the garbage collector runs, it
checks for session files that haven't been accessed for at least session.gc_maxlifetime
and deletes them.
This is all explained pretty well in forum posts (this one especially!) - But the
following questions do come up:
1.) How is that probability applied? When does the server roll the dice?
A: The server rolls the dice every time session_start() is called during
any active session on the server. So this means you should see the garbage
collector run roughly once for every 100 times that session_start() is called
if you have the default of session.gc_probability = 1 and session.gc_divisor = 100
2.) What happens on low volume servers?
A: When session_start() is called it FIRST refreshes the session and makes the
session values available to you. This updates the time on your session file on the
server. It THEN rolls the dice and if it wins (1 out of 100 chance) it calls the garbage collector. The garbage collector then checks all session id files and sees if there are
any that are eligible for deletion.
So this means that if you are the only person on the server, your session will
never go inactive and it will appear as though changing the settings have no
effect. Let's say you change session.gc_maxlifetime to 10 and session.gc_probability
to 100. This means there is a 100% chance the garbage collector will run and it
will clear out any session files that haven't been accessed in the last 10 seconds.
If you're the only one on the server, your session will not be deleted. You need
at least 1 other active session running for yours to go inactive.
So basically, on a low volume server or at a low volume time - it could be MUCH
longer than session.gc_maxlifetime before the garbage collector actually runs and
the sessions are actually deleted. And without knowing how this works, it may
appear completely random to you.
3.) Why do they use the probability?
A: Performance. On a higher volume server you don't want the garbage collector
running on every request of session_start(). It will slow down the server
needlessly. So depending on your server volume, you may want to increase
or decrease the probability that the garbage collector runs.
I hope that this ties things together for you. If you're like me and you tried
session.gc_maxlifetime and it didn't seem to work (because you tried it
out on a development server so as not to disturb anyone), then this post
hopefully saved you some head scratching.
Good luck!
Each time session_start is called the session files timestamp (if it exists) gets updated, which is used to calculated if session.gc_maxlifetime has been exceeded.
More importantly you can't depend on a session to expire after session.gc_maxlifetime time has been exceeded.
PHP runs garbage collection on expired sessions after the current session is loaded and by using session.gc_probability and session.gc_divisor it calculates the probability that garbage collection will run. By default its a 1% probability.
If you have a low number of visitors there is a probability that an inactive user could access a session that should have expired and been deleted. If this is important to you will need to store a timestamp in the session and calculate how log a user has been inactive.
This example replaces session_start and enforces a timeout:
function my_session_start($timeout = 1440) {
ini_set('session.gc_maxlifetime', $timeout);
session_start();
if (isset($_SESSION['timeout_idle']) && $_SESSION['timeout_idle'] < time()) {
session_destroy();
session_start();
session_regenerate_id();
$_SESSION = array();
}
$_SESSION['timeout_idle'] = time() + $timeout;
}
session.gc_maxlifetime is based off of the last time a session file was modified. So every time a session file is modified or a session_start() is called in a separate page, the countdown to gc_maxlifetime begins anew and the user stays "logged in". This is the value you are looking for. You can modify this through ini_set() in your php files, or edit php.ini if you have access to it
session_cache_expire() only controls the HTTP "Expires" header. This header controls how long the downloaded page contents stay in the user's browser cache.
To check the current values, this code will be helpful:
$gc_maxlifetime = ini_get('session.gc_maxlifetime');
$gc_probability = ini_get('session.gc_probability');
$gc_divisor = ini_get('session.gc_divisor');