Making PHP session expire (in Drupal and in general)

Submitted by boaz on Tuesday, January 6, 2009.

As the Internet evolves, so do the black hats that abuse the net and its inhabitants to their needs. A very common requirement therefore these days is to limit the session lifetime in a PHP application. PHP provides several directives in php.ini to achieve the desired configuration. This post will focus on how to have session expiration in Drupal (at least it mention most ways to do it, I think) but any PHP programmer unfamiliar with this issue could be enlightened by it (I think).

First, some background:
In PHP, session expiration consists of two sides - client and... that's right, server side smiley.

  • In client side, we simply tell the browser when the cookie becomes invalid - its "lifetime". This is easily achieved by setting a php ini setting of session.cookie_lifetime. The value set for this will tell the browser when the cookie is considered dead and it should throw it (more here). A value of '0' means (for current session only - meaning on browser shutdown).
  • Now to server side: on server side the picture is a bit more complex, and I must note that the PHP documentation does too little to clear the obscurity (as noted in comments in the PHP manual and in contrast to most text on the official PHP manual which is rather (damn) good for an OSS documentation). On server side, you have several php ini directives which control the session expiration:
    1. session.gc_probability
    2. session.gc_divisor
    3. session.gc_maxlifetiime
    All the above 3 are related and they work like this:
    • on every request(*), the session "garbage collector" is checked by PHP if it needs to run. It runs on gc_probability/gc_divisor part of the requests. For example, if probability is set to 1 and divisor is set on 50, the garbage collector will run on every 1/50 of the requests PHP gets.
    • When the garbage collector runs, it runs the current PHP registered "session GC" function, handing over to it the parameter of session.gc_maxlifetiime. This function's purpose is to actually check all current sessions, determine who needs to be deleted, and actually delete it.Why am I being non-specific? Because its very common these days that your PHP application is "registering" its own session handling functions and implement that "session GC" function. For example, Drupal saves its sessions in its database. its registered session GC function runs the following command:
      db_query("DELETE FROM {sessions} WHERE timestamp < %d", time() - $lifetime);
  • Well ok then, you might say, "nice to know; where's the catch?". Read on... .

The catch:

Modern Linux distro's, say Ubuntu (8.10) comes with a default PHP configuration of session.gc_probability = 0. This means that in general, session GC will never run(!). There is a unix cron job that runs every half hour and checks for old sessions to delete (see more at /etc/cron.d/php5) but this is done only if you're using the standard "files" as your session handler (check out the directive of session.save_handler on your machine to learn what php default sessions handler is but make sure to check that your application doesn't overwrite this), so for Drupal users using plain Debian/Ubuntu, stock LAMP packages and stock Drupal applications, this means that the PHP garbage collection, despite your best intentions, will also never run.

The solution:

  • In order to still have session garbage collection you'll probably want to have the following in your settings.php of your Drupal's site (note that there's already a section of ini_set in that file - just update it) :
ini_set('session.cookie_lifetime', 0); // browser cookie deletion on browser close
ini_set('session.gc_maxlifetime', 600); // 10 minutes
ini_set('session.gc_probability', 1); // see next line...
ini_set('session.gc_divisor', 100); // in combination with previous
// ini_set, this will make GC run
// on 1% of the requests
  • Well, the above is just one way to do it. You can also implement a cron job (unix) that runs every, say, 5 minutes, and runs a very similar mysql query as quoted above (from Drupal's session GC function).
  • Another option for Drupal users is to use session expire module which uses Drupal's cron facility to delete the sessions, along with some comfortable configuration options in Drupal's admin menu.
  • Dont forget the client side option which we started with - the session.cookie_lifetime ini directive. Instead of having it set on zero as suggested above, we could have it set on 600 seconds (10 minutes again). I didn't use this yet but I believe that will aid as well. But its important to remember that this is of course should only be considered as a "recommendation" to the browser. A malicious user could easily mangle the cookie and alter its lifetime. As always when it comes to security - the last thing you'll want to depend on is the client side (unfortunately, I should add).

Resources:


  • A bit more on the discussion about this in the Drupal community can be seen here: http://drupal.org/node/72856
  • Thanks to Gal Gure-Arie for the pointer on the unix cron job.

Boaz.

 

Leave a Comment

Fields with * are required.