By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,257 Members | 928 Online
Bytes IT Community
Submit an Article
Got Smarts?
Share your bits of IT knowledge by writing an article on Bytes.

Setting a timeout for PHP sessions.

Atli
Expert 5K+
P: 5,058
Introduction
In this article I aim to explain the reasons why PHP sessions expire after a set period of inactivity, and how this mechanism works. I'll describe two commonly used methods to control the lifetime of a session, and show how to do so in your code.

The problem
PHP's session mechanism allows us to store data for clients on the server to make it persist through multiple requests. However, because the data itself is stored on the server, and the only thing connecting client's to their respective sessions on the server is a simple cookie, this creates a major security concern.

An exploit known as session hijacking is when a malicious third party intercepts or steals the session cookie from a client, and uses it to access the data from an active session. The server can do little to defend against this because it has no way to tell the original client from the hijacker.

The solution
The most important defense we have against this is to disregard sessions that have been inactive for a set period. This prevents hijackers from accessing sessions unless they are doing so in real-time, within the specified period.

In PHP the mechanism that implements this is known as the session garbage collector. Data files are tagged with a "last modified" time stamp, which PHP uses to determine when the client last used a session. If the session is older than the allowed lifetime of a session, it is destroyed. The default lifetime of a session in PHP is 1440 seconds, or 24 minutes.

We may find ourselves in situations where we would want to manually configure the session lifetime; to be able to better control the time-out period of a session. There are two ways we can do that:

Method #1: Code it manually
You can simply add a snippet of code to the top of every page on your site that sets a field in the session to the current time. You can then use that field to determine how long the session has stayed inactive for and act accordingly. For example:
Expand|Select|Wrap|Line Numbers
  1. session_start();
  2. $timeout = 60; // Number of seconds until it times out.
  3.  
  4. // Check if the timeout field exists.
  5. if(isset($_SESSION['timeout'])) {
  6.     // See if the number of seconds since the last
  7.     // visit is larger than the timeout period.
  8.     $duration = time() - (int)$_SESSION['timeout'];
  9.     if($duration > $timeout) {
  10.         // Destroy the session and restart it.
  11.         session_destroy();
  12.         session_start();
  13.     }
  14. }
  15.  
  16. // Update the timout field with the current time.
  17. $_SESSION['timeout'] = time();


Method #2: Reconfigure PHP's session garbage collection
The session.gc_maxlifetime PHP.ini directive controls how long a session is allowed to exists before it is considered garbage and is cleaned up. Every call to the session_start() function has a chance to trigger the garbage collection routine.

The chance each call has to trigger the routine is determined by the session.gc_probability and session.gc_divisor directives. The probability is calculated as:
- session.gc_probability / session.gc_divisor

By default the values are 1 and 100, respectively, or a 1% chance that the garbage collector is triggered. If your site has low traffic, you should increase the value of session.gc_probability to increase the chance. If you want to guarantee that it will be triggered, set both directives to the same value.

Another thing to consider is the directory where PHP stores the session data. PHP applies the same lifetime to all session data files in the same directory; the lowest lifetime applies to them all. Meaning that if you want to be sure your session is timed out correctly, you need to be using your own directory. Best way to deal with that is to either create one in your home or temp directories. You can have PHP create the directory using the mkdir function, and you set the location by changing the session.save_path directive.

These values can be set using the ini_set function, so you can set the session garbage collection values on a per-request basis. For example, this function can be used to start a session and have it time out using a given time-out value:
Expand|Select|Wrap|Line Numbers
  1. <?php
  2. /***
  3.  * Starts a session with a specific timeout and a specific GC probability.
  4.  * @param int $timeout The number of seconds until it should time out.
  5.  * @param int $probability The probablity, in int percentage, that the garbage 
  6.  *        collection routine will be triggered right now.
  7.  * @param strint $cookie_domain The domain path for the cookie.
  8.  */
  9. function session_start_timeout($timeout=5, $probability=100, $cookie_domain='/') {
  10.     // Set the max lifetime
  11.     ini_set("session.gc_maxlifetime", $timeout);
  12.  
  13.     // Set the session cookie to timout
  14.     ini_set("session.cookie_lifetime", $timeout);
  15.  
  16.     // Change the save path. Sessions stored in teh same path
  17.     // all share the same lifetime; the lowest lifetime will be
  18.     // used for all. Therefore, for this to work, the session
  19.     // must be stored in a directory where only sessions sharing
  20.     // it's lifetime are. Best to just dynamically create on.
  21.     $seperator = strstr(strtoupper(substr(PHP_OS, 0, 3)), "WIN") ? "\\" : "/";
  22.     $path = ini_get("session.save_path") . $seperator . "session_" . $timeout . "sec";
  23.     if(!file_exists($path)) {
  24.         if(!mkdir($path, 600)) {
  25.             trigger_error("Failed to create session save path directory '$path'. Check permissions.", E_USER_ERROR);
  26.         }
  27.     }
  28.     ini_set("session.save_path", $path);
  29.  
  30.     // Set the chance to trigger the garbage collection.
  31.     ini_set("session.gc_probability", $probability);
  32.     ini_set("session.gc_divisor", 100); // Should always be 100
  33.  
  34.     // Start the session!
  35.     session_start();
  36.  
  37.     // Renew the time left until this session times out.
  38.     // If you skip this, the session will time out based
  39.     // on the time when it was created, rather than when
  40.     // it was last used.
  41.     if(isset($_COOKIE[session_name()])) {
  42.         setcookie(session_name(), $_COOKIE[session_name()], time() + $timeout, $cookie_domain);
  43.     }
  44. }
To set a one minute timeout on a session, you could call the above function like so:
Expand|Select|Wrap|Line Numbers
  1. session_start_timeout(60);
By default, it sets a 100% chance the GC routine triggers. On a high traffic site that could put strain on the server. To fix that, you could lower the chance by passing the percentage you want in the second parameter:
Expand|Select|Wrap|Line Numbers
  1. session_start_timeout(60, 10);
This gives it a 10% chance to trigger the GC routine.

Epilogue
If you have any questions or comments about the article, feel free to leave a reply. If you need help fixing specific problems, I urge you to post a new question in the PHP answers forum, where the entire community will do it's best to help you out.

All the best,
- Atli
Jun 7 '10 #1
Share this Article
Share on Google+
4 Comments


erikschoel
P: 1
Very nice. Clear and concise. Thanks!
Mar 14 '12 #2

P: 1
I want to destroy a session when there is no user activity up to some amount of time say 15 minutes, how i could make a session that checks whether there is any key press event or not??
Aug 11 '14 #3

Atli
Expert 5K+
P: 5,058
If you want to monitor what's happening on the browser, that would require JavaScript. PHP can't interact with the browser, aside from generating the initial HTTP response content.

The first step of this would be to set the session mechanism up so it destroys the session after your desired timeout period. My above post explains how that works.

The second step - if you want every key and mouse interaction to refresh the session timeout - would be to use JavaScript to listen for all key and mouse events. It should be sufficient to listen for "keydown" and "mousemove" events on the document object. - In that listener, you'd then want to "ping" the PHP code with an AJAX request, so that it will keep the session alive on the server. What I mean by that is: set up a PHP script who's only purpose is to refresh the session, and then have JavaScript request that script on events.

Also, to avoid flooding the server with pings, when your users are typing rapidly or moving the mouse around a lot, you'd want to take measures to make sure AJAX requests aren't being sent tens or hundreds of times per second. (Which can easily happen with the mousemove event.) Make sure you keep track of when AJAX requests are being fired, and that it's only happening once every few seconds.


One more thing. JavaScript event propagation is a bit of a tricky thing, and it is possible to stop events from reaching the document object. If you do that anywhere in your code (or are using poorly built plugins!) then the affected events won't refresh the session.
Aug 13 '14 #4

P: 1
Merry Christmas!
I tried this code to check timeout, but always get the same session id:
Expand|Select|Wrap|Line Numbers
  1. print session_name();
Is this normal?
Dec 26 '14 #5