I have an intranet site that has to perform complex and lengthy MySQL operations, and I have to find a way to prevent a PHP Session timeout from happening while this is going on.
A PHP page has all the form data that the user can fill out. When the user hits the submit button, an Ajax call goes out with that data, and the Ajax call waits until the operation is completed and then notifies the user of the result. I have to prevent the site from timing out while this call is going on.
My first thought was to use setInterval and run a second ajax call every 10 seconds to a page that only loads the session and refreshes some of the data in it, but during a first test I noticed that the second call and any further calls were not answered until the first one completed (I used PHP sleep(20) in my first call to simulate a long wait). My script started several new ajax calls (which I watched in Firebug), but they all waited until the first call was completed - there was no response from the server until then. I think the server simply ignores a second call from the same client until the first is done. I doubt Apache would stop accepting calls because PHP is waiting for sleep to finish...
During my research on this I haven't really found out yet what event is used to consider a Session timeout. One page claimed that just using Ajax to load a picture would extend the Session time, but that doesn't sound right to me since that doesn't involve PHP - besides, if the server doesn't accept a second connection, that won't work. Another page said it has to do with whenever the Session variable data is being written do, but again that also doesn't match my experience since I have plenty of pages that write during login but not after that.
So my question is two-fold:
Which exact events have to happen or not happen within a given timeout period for the Session to be considered "timed out"? I am guessing there are two timers involved here - the cookie timeout in the browser and the Session timeout in PHP, but these are just guesses, and I'd like to hear from the experts on this.
How can I keep that Session alive while the process is still going on? The process may take an hour or longer. The site is an intranet site and not accessible from outside the network, so security is not quite as big an issue, but I still want the regular Session timeout to work outside of this page.
OK, I feel the need to clarify here.
I am trying to find out what happens behind the scenes on the server whenever a PHP session timeout is involved.
For example: If I have a script that takes 1h to create a PDF file but the timeout is set to 30 minutes, will the timeout be triggered because the user/browser/mouse is not active, or will the timeout NOT be triggered because a script is still running?
What I would like to know is what EXACTLY is going on - what events take place (both user and server created) in prolonging a session before it times out?
You can tell me about PHP settings all day, and I still won't know what happens.
Let's start with the basics as far as I understand them:
A typical session may consist out of a session file on the server, a session id, and often a cookie in the client browser that holds the session id and a timestamp.
I'm assuming that the session file on the server or some index of session files also includes a timestamp.
Which events cause these timestamps to be updated, and which of these timestamps is being used to determine that the session has timed out or not? I could see the server testing the cookie's expiration date to determine if the session should timeout or not, but it's probably safer to rely on the server information.
This is the type of information I'm looking for here.
Maybe I should close the original question and write a new one with this information - I'm open to suggestions here. When I wrote the original post, I just needed an answer for my project, but I realized that I will never fully understand the process until I know the workings in the background.
First, you have to know that while the user is active (browsing your Intranet by classic Link, or by ajax, because apache server can not make a distinguish) your sessions still alive.
If user become inactive after a period of time, or disconnect from your Intranet, so the minimum of time that the session will be destroyed will be tha value of the parameter that was set in php.ini :
session.gc_maxlifetime= TIME_IN_SECONDS
To configure properly the session time out you have to configure these 3 parameters :
; Defines the probability that the 'garbage collection' process is started
; on every session initialization. The probability is calculated by using
; gc_probability/gc_divisor. Where session.gc_probability is the numerator
; and gc_divisor is the denominator in the equation. Setting this value to 1
; when the session.gc_divisor value is 100 will give you approximately a 1% chance
; the gc will run on any give request.
; Default Value: 1
; Development Value: 1
; Production Value: 1
; http://php.net/session.gc-probability
session.gc_probability=PROBA_CHANGEME
; Defines the probability that the 'garbage collection' process is started on every
; session initialization. The probability is calculated by using the following equation:
; gc_probability/gc_divisor. Where session.gc_probability is the numerator and
; session.gc_divisor is the denominator in the equation. Setting this value to 1
; when the session.gc_divisor value is 100 will give you approximately a 1% chance
; the gc will run on any give request. Increasing this value to 1000 will give you
; a 0.1% chance the gc will run on any give request. For high volume production servers,
; this is a more efficient approach.
; Default Value: 100
; Development Value: 1000
; Production Value: 1000
; http://php.net/session.gc-divisor
session.gc_divisor=DIVISOR_CHANGEME
; After this number of seconds, stored data will be seen as 'garbage' and
; cleaned up by the garbage collection process.
; http://php.net/session.gc-maxlifetime
session.gc_maxlifetime= MAX_LIFE_TIME_CHANGEME
Related
Hello everyone,
This is mostly a proof-reading question, if I may put it like that.
So, I need to create a persistent session for a webshop/e-commerce site. A session lasting over the specified time is not really an issue, but making it last for, say 24 hours since the last click, is important.
Why: To quickly check cart status, last visited pages, tax- and currency choices etc. without querying, let alone writing to the database or some other storage mechanism on each page request. The cart and browsing history are complicated enough to be stored in a session, Language, currency etc. could admittedly be cookies as well.
Most of our site is cached as html, and only a few elements like cart contents are strapped to the otherwise finished page. We have very good server response times, and I'd like to keep them that way and avoid anything too heavy especially for users who don't have anything in their cart.
I've gone through quite a few posts on StackOverflow and Google in general to get a basic idea of this. Most results were about being concerned and making sure the session would close in any case after a certain period of time. This case is the opposite.
So, PHP has a few session setting in the ini, of which three are the most important:
session.gc_maxlifetime: Determines the time after which a garbage collection cycle will remove the file. This is not an exact time, it
is a minimum time for the session to last since the last change of
the $_SESSION variable data. Default is 1440 seconds / 24 minutes.
session.cookie_lifetime: This is the lifetime for the PHPSESSID -cookie which enables PHP to fetch a corresponding session. This defaults to zero, which means the cookie is destroyed when the
browser closes. It could thus live for one millisecond or two years
if set to zero.
Setting this to an integer value will give the cookie an expiration
from the time it is first set. The lifetime will not be forwarded on subsequent page loads with session starts.
session.save_path: Tells PHP where to save the session files. This is also the directory which will get garbage collected for
expired sessions.
So here's an example "session header" I came up with. It should at least ensure session files are stored for at least $lt seconds from the first session start, and the session cookie $lt seconds from the last execution of this script. In the question -comment the session gets written to, which would in my thoughts cause the file time to get updated, and thus push session expiry to $lt since the last execution of the script.
<?php
/**
* Example "header" to start a session in a simple environment
*/
// Lifetime setting in seconds
$lt = 1440;
// Set maxlives to $lt
ini_set('session.gc_maxlifetime', $lt);
ini_set('session.cookie_lifetime', $lt);
// Save sessions in a different directory than other sessions. The gc
// will run on a probability base on each session start and clean the default
// directory. If our (longer) sessions are saved in a shared session dir
// for other PHP scripts which have a shorter expiry, our files will also be
// removed.
// If we run a single site, use php.ini to set the above max lives,
// or are the only ones using sessions, we can omit this directive.
ini_set('session.save_path', '/var/www/mysite/separate-session-dir');
// Start the session AFTER setting the parameters
session_start();
// Update the cookie expiration on a page load.
// This will simply overwrite the system-set session cookie.
setcookie(session_name(),session_id(),time()+$lt, '/');
// QUESTION: Would this forward the session FILE lifetime by writing to it.
$_SESSION['random-field'] = uniqid();
// Do what you really meant to do with your script.
So. Here it is. Does this work as a simple way to ensure sessions live for at least $lt seconds, or have I overlooked something?
I am currently running this on our server, without the ini_sets and save path as they are set in the php.ini instead. The cookie part seems easy enough to verify by closing the browser and returning to see if a cart is emptied or not, but the file -part is harder to verify and I'd like your input about this.
PS. Sorry about stuffing e-commerce/webshop tags, but I searched for hours for a post which would look at things from this perspective.
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.
I have an issue with session , my session is automatically destroyed after few minutes of inactivity ,i think its must be 24 minutes i.e. 1440 seconds.I want to session remain for a long time , i am using .user.ini file on the server and set session.gc_maxlifetime to 31557600 seconds and session.cookie_lifetime to 31557600 but nothing happened for me .Its still logout after 1440 seconds of inactivity .I have also attached session value of png image of phpinfo.
I hope your answer or any help will work for me.Thanks.
I'm not sure why your settings aren't working but I use the following on my scripts to overwrite the php.ini settings for session maxlifetime and session_save_path:
session_save_path('/pathto/writable/dir/on/your/account');
ini_set('session.gc_maxlifetime', 24*60*60); // 24 hours; change as necessary
session_start();
NOTE:
The session_save_path is important because the default path is /tmp and it may get deleted by the system administrator on a daily/week? basis.
In my experience, it's always been related to the php.ini file. In your case, the master and local values for session.gc_maxlifetime contradict. (gc stands for garbage collect) They don't have to agree with each other, since the local value is used for the running script. It just means there's two php.ini files on your system, located in different places, and the local php.ini file is overriding the master php.ini file settings. But, I'd be VERY suspicious of any files on your server which call session_start() which use the master php.ini file, or call ini_set(...) within the script itself. The way this works is that no matter what the value is set to, it only has meaning when it's time to do garbage collecting. And garbage collecting is done by session_start() but you can also trigger garbage collecting in other ways such as SessionHandler::gc or a cronjob as explained later in this post. When called, it checks the last modified time of the file on the server storing your session information. If the number of seconds that elapsed since then is greater than the current session.gc_maxlifetime value, it will destroy the session. Note this is the last modified time, not the last accessed time, so you'll want to change your session data frequently to prevent it from getting deleted if it's not changing. You should also be aware that there is a setting here, called session.lazy_write which, if enabled, and is enabled by default, WILL NOT update the last modified time of the session file in the event which the session data did not change. Thus you'll want to disable this if you want to minimize the chances of sessions being destroyed early for some unknown reason, or store a timestamp on the session so the data is always changing and you know when the session was last used, if old, you can manually call session_destroy(). To start another session, you can commit with session_write_close() then recall session_start(). Or, do all 3 at once with session_regenerate_id(true).
Nextly, if you initialize a session with session_start() with your intended settings, and continue to call session_start() with the intended settings with each request, awesome. But, once any file on your server calls session_start() with a different value for session.gc_maxlifetime, either from using the master php.ini value in your case, or the script calling ini_set(...) and ignoring the master value, it will check the file's last modified time against a different value and destroy your session despite your intended settings - is assuming it gets elected to be one of the 1 in 100 requests which have to garbage collect.
Another thing to be concerned with is session.cookie_lifetime. A value of 0 here turns the cookie into a browser session cookie. This means if the user closes their browser, then the session cookie will be deleted by the browser. Your master value is using 0. But your local value is using 31557600 (the average seconds in a year). So you should be fine here. But keep your eyes open if any scripts on your server override this value, use a value of 0, or use the master php.ini file.
You should also be aware of the default 1% garbage collecting CHANCE that a session will be destroyed as defined by session.gc_probability and session.gc_divisor which default to 1 and 100 respectively. Garbage collecting is done when start_session() is called, if, and only if, the request "randomly" gets picked to be the request to manage the Garbage Collecting. This means that even if the number of defined seconds for a session elapsed for it to expire, start_session() STILL won't garbage collect even for this expired session. Rather, most users will notice their sessions expire exactly to schedule due to the cookie the browser keeps track of having its timestamp expire. But the session isn't enforced until PHP garbage collects as per the garbage collection change when start_session() is called. If you want sessions to be wiped clean when they've expired, and start a new one, you should use session_regenerate_id(true). The true here means trash the $_SESSION data tied to the previous session and toss them a different session id as though their session expired.
You should also be aware that some systems, such as debian-based systems, have a cronjob which runs every 30 minutes to garbage collect based on the master php.ini configuration information.
See the comment here by Christopher Kramer: http://php.net/manual/en/session.configuration.php
I cannot verify if the above information about debian systems is true, but I've considered garbage collecting with a cronjob before to speed up users' requests so no one user gets stuck having to wait for their request to go through due to the maintenance which a cronjob could be handling.
In terms of solutions here:
One solution here is to adjust the master php.ini value, if you have access to it, then search your server for any PHP files which might be calling ini_set to see if there's any files conflicting with your settings to make sure they're not causing the unexpected behavior.
Another solution would be to limit such conflicts the script might be encountering by: (1.) renaming the session.name to something other than PHPSESSID. And/or (2.) changing the session.save_path path. Either of these by itself would suffice and avoid script conflicts.
A temporary fix might be to do something like change your session.gc_probability to 0 so the session garbage collecting NEVER happens. Or make it a much smaller chance by using something like session.gc_probability=1 and session.gc_divisor=100000. Then setup a cronjob to call SessionHandler::gc
See http://php.net/manual/en/session.configuration.php for more session config information.
Lastly I'd like to point you to this post which suggests good practices to prevent session hijacking, and for the most-part is the post I referenced when putting this post together: https://stackoverflow.com/a/1270960/466314
Note that it uses an approach to sessions which makes sure sessions expire on time, and not later (although browsers do a good job of this already with cookie garbage collecting). And it also makes changes to sessions, keeping track of the session last used time, so the session data is always changing to avoid the issue with session.lazy_write.
This concludes my suggestions. If you can narrow down the issue, try searching stackoverflow or asking a new question.
As stated here :
The best solution is to implement a session timeout of your own. Use a
simple time stamp that denotes the time of the last activity (i.e.
request) and update it with every request:
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 1800)) {
// last request was more than 30 minutes ago
session_unset(); // unset $_SESSION variable for the run-time
session_destroy(); // destroy session data in storage
}
$_SESSION['LAST_ACTIVITY'] = time(); // update last activity time stamp
Updating the session data with every request also changes the session
file's modification date so that the session is not removed by the
garbage collector prematurely.
I'm setting up a website, and I'd rather not put cookies on people's computers. Is it bad practice - or worse- insanely insecure to extend the sessions max timeout to a day or two days?
session_regenerate_id();
$profileid = $userdata['userid'];
$profile = $userdata['username'];
//Set session
$_SESSION['profileid'] = $profileid;
//Put name in session
$_SESSION['profile'] = $profile;
$_SESSION['loggedin'] = true;
Edit: Added code.
Edit: the php.ini line that I would modify is:
session.gc_maxlifetime = 1440
session.gc_maxlifetime
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).
This value (default 1440 seconds or [24 Minutes]) defines how long an unused PHP session will be kept alive.
For example: A user logs in, browses through your application or web site, for hours, for days. No problem. As long as the time between his clicks never exceed 1440 seconds. It's a timeout value,
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.
So increasing this value will most likely not have much effect on your script unless you expect your users not to click around your site. Like in the case of a logged in user watching a long video and then after watching find themselves logged out afterwards. If this is the case perhaps you should use a some javascript to poll the server every 20mins to keep the session open.
I don't think there's anything particularly insecure about it, as long as your code in general is secure.
However
a) The user will lose their data if they close the browser - which they might do for any reason. If you need to ensure that data is kept around for a longer period, between sessions, unless the user specifically opts out, use cookies. Otherwise, it seems kind of counter-productive to insist on no-cookies. To clarify, I think what I mean is, if the standard session lifetime isn't long enough, then you probably shouldn't be using $_SESSION to store the data. By it's nature, $_SESSION is ephemeral and can only be used for fairly ephemeral things.
b) Unless you're really going an extra mile to prevent all cookies, the PHP Session id will be set using a cookie anyway, so are you really doing your users any favours?
I am trying to increase the SESSION time so that the users do not time out for 12 hours. This is a private site used only by employees and the timeout is annoying and it causes partially filled out data to be lost. According to what I read the following code should work:
ini_set('session.gc_maxlifetime',12*60*60);
ini_set('session.gc_probability',1);
ini_set('session.gc_divisor',1);
session_start();
but it has no effect. Any thoughts?
thanks
ini_set('session.gc_probability',1);
ini_set('session.gc_divisor',1);
These two are forcing PHP to run the session cleanup script for EVERY hit on your site. PHP's formula to run the gc is:
if (random() < (gc_probability / gc_divisor)) then
run_session_garbage_collector()
}
Right now you've got 1/1, so 100% chance of the garbage collector being run. While extending the timeout period is good, you also want to REDUCE the chance the collector runs at all. Otherwise you're forcing PHP to do a full scan of ALL session files and parse EACH one, for EVERY hit on your site, to find the odd one or two that might have expired.
Try setting the gc_probability to 0 to completely disable the garbage collector.
As well, be aware that changing the settings, as you are, within a script with ini_set() doesn't change the timeouts/defaults that OTHER scripts use. This script might have a longer timeout and changed probability, but if other scripts hav ethe default timeout (10 minutes or something), then they'll happily nuke any "stale" sessions.
The proper place to set session timeout/cleanup settings is at the php.ini level, so that it applies to all scripts on the site, not just this single script of yours.
You could try setting the session.gc_maxlifetime = 12*60*60 in your php.ini file.
Otherwise when your script ends the session.gc_maxlifetime variable will be reset each time.
from php.net/ini_set :
string ini_set ( string $varname , string $newvalue )
Sets the value of the given configuration option. The configuration option will keep this new value during the script's execution, and will be restored at the script's ending.
The session cookie might be expiring as well: session.cookie-lifetime
I'm not sure if by "private site" you mean its alone on a dedicated server, but if not:
If your session temp files are being stored in the same directory as other sites' tmp files, its possible that a lower session lifetime on another website is triggering garbage collection, which could delete your "private site's" session. Depends on server setup. Personally I've got a tmp folder for each client to avoid things like that.
session.gc_divisor coupled with session.gc_probability defines the probability that the gc (garbage collection) process is started on every session initialization. The probability is calculated by using gc_probability/gc_divisor, e.g. 1/100 means there is a 1% chance that the GC process starts on each request. session.gc_divisor defaults to 100.
So your doing 1/1 - I'm not sure how that will actually function, but it could be odd behavior.
Hope this will help you. just copy and paste the bellow code in web.config file
<sessionState mode="InProc" stateNetworkTimeout="10" sqlCommandTimeout="30" cookieName="ASP.NET_SessionId" timeout="53200" regenerateExpiredSessionId="false">
<providers><clear/></providers>
</sessionState>