Tuesday 14 June 2011

How PHP Sessions Work

Recently I was trying to debug some code that was monitoring the number of current sessions for various user groups. Along the way I found out a bit more about how PHP sessions work, so I thought I'd document it here and maybe someone else will find it useful.

Starting a Session
I recommend the following to start a session in PHP:

if (!session_id()) {
    session_start();
}

This works in all situations (whether or not session.auto_start is enabled) and won't produce a E_NOTICE if the session has been previously started. It also looks tidy and doesn't require the error suppressor '@' which I prefer to avoid.

How Sessions are Stored on the web server
By default the sessions are stored in files in a temporary file location, on my Ubuntu desktop this is /var/lib/php5. You can get or set this value with the session_save_path function or the session.save_path runtime setting. If you don't want to use files to store session information you can also define a custom storage handler, for example a database, I'm just using the PHP default here.

Each session has a unique id associated with it, you can retrieve this with the session_id function, this id is used as the filename prefixed with 'sess_', for example sess_846a738ce3a570964c4d70fdc198bc6d. The session variables are stored within the file, for example the code:

$_SESSION['user'] = 'ewen';
$_SESSION['logged_in'] = true;
$_SESSION['id'] = 159753;

Produces file contents of:

user|s:4:"ewen";logged_in|b:1;id|i:159753;

For more information on how PHP variables are serialised as strings see the serialize function.

How Sessions are linked to a Request
For the web server to know which requests are linked to which session the client must store the session id. The best and most common way to do this is to store it in a cookie, by default it will be stored with the name PHPSESSID, you can modify this with the runtime setting session.name or the function session_name. By default the session cookie will expire once the browser is closed, of course this can also be modified with a runtime setting - session.cookie_lifetime.

PHP also has a fall back mechanism to pass the session id as a URL parameter if cookies are unavailable, since PHP v5.3 this is disabled by default as it makes it very easy for your session id to be captured and your session hijacked. I recommend that you make sure the runtime setting session.use_only_cookies is set to 1.

Some Tips on Saving to the Session
  • Don't save resources to the session, for example a database connection or file handle.
  • Don't save large amounts of data to the session, the overhead of a session is fairly high when using files as the storage handler. For example, instead of storing all of the current user's data in the session store just their user ID and use that to retrieve the other needed user data from a database. An exception to this rule is for multi-page forms where it makes sense to store in the session so you can process it into the database in a single atomic transaction.
  • If storing objects to the session, make sure that the class definition is available when retrieving it from the session otherwise there may be errors when unserializing the object.

Hijacking Sessions
To hijack a session all you need is the session id. Here are some ways that the session id might get captured:
  • Monitor the traffic (if it's unencrypted), see the screenshot below.
  • If you have shell access to the web server you can list files in the sessions directory (even if you can't read the file the session id is stored in the filename).
  • Get the session id from the cookie stored by the users browser.
  • If the session id is being passed in the URL, clicking on an external site link could make the id available to the other site through the HTTP header field 'referrer'.
Wireshark traffic capture showing the Session ID

Once you know a session id, you can create a cookie using a browser extension (I use Edit This Cookie for Chrome), then simply visit the site and you will now have hijacked the previous users session.

Editing the PHPSESSID cookie


Session Expiry and Garbage Collection
Sessions expire in PHP when the client's browser deletes their cookie, by default this is once they close their browser. However, the users session data on the server gets garbage collected on the server after a certain amount of time. This is done in two steps:

1. Session Data is flagged as 'garbage'. After session.gc_maxlifetime seconds, the session data is seen as 'garbage' and is flagged to be removed next time the garbage collector is run. By default this occurs 1440 seconds or 24 minutes after the session files last access time. Importantly, even though the data is 'garbage' doesn't necessarily mean it will be deleted any time soon.

2. The Garbage Collector is run and deletes 'garbage' session data. Every time a session is initialised there is a chance the garbage collector will run. By default the probability it will run is 1%, the probability is calculated using two runtime configuration values, session.gc_probability and session.gc_divisor. The probability (default value 1) is divided by the divisor (default value 100) to get the percentage change e.g. 1 / 100 = 0.01 = 1%. Therefore even though a users session has been idle for longer than the max lifetime value their session data may live much longer, so if you're implementing a site where the user's session should expire after a precise length of time you'll need to do more than just set the max lifetime.


Destroying Sessions
To destroy a users session data you can use the function session_destroy. Note that this doesn't instantly unset all of the session data for the rest of the request so you might want to redirect the user after running session_destroy or empty all of the session variables.

session_destroy(); // destroy the file containing the users session data
$_SESSION = array(); // optionally clear the session variables for the rest of the script execution

If you want to totally kill the session, i.e. unset the users session id, the session cookie must also be deleted (assuming cookies are being used to propagate the session id), the session_destroy page from the PHP manual has some sample code on deleting the session cookie. Personally I've never found a need to destroy the session id/cookie as clearing the session data is enough.

More Resources

Thursday 2 June 2011

Spaghetti Western Gnome 3 Review

About a month ago I upgraded to Gnome 3 on my Arch Linux laptop.

Despite all the complaining in the Linux world about the new Gnome 3 and Unity interfaces I'm mostly happy with it so far, but there are some things that annoy me. So, lets break this down Eastwood style.

The basic desktop.


THE GOOD

1. It looks nice.
I like the user interface, for me it hits a good balance between eye candy and responsiveness. Watching early screencasts of Gnome 3 I thought that the eye candy looked mostly gratuitous, but after running it myself for a while, it does all tie in nicely with the desktop functionality. So points for Gnome's design team.

2. It's Stable.
I can't speak for other users but I've been running this on Arch with the open source ATI video driver and can't remember encountering a single bug or random crash, which is impressive for such a major version leap (KDE 4 springs to mind).

3. Window Management is good.
Finally a popular Linux desktop environment that has good window management. Comparing two windows side by side and maximizing windows are now done by very intuitive mouse gestures. To maximize just drag the window to the top of the screen, to maximize it to half of the screen drag the window to the left or right. Simple and awesome. The other thing is a pretty cool Mac-like window overview which I really like. I think these features are the biggest win over Gnome 2.

The Mac-like window overview.


THE BAD

1. Launching Applications is clumsy.
For people who haven't yet seen Gnome 3 this is hard to explain but you need to move the cursor to Activities at the top-left of the screen then click Applications, then select a category, and then select your app. A lot of mouse mileage. For common apps you can add them to a dock on the left hand side which makes launching much faster. However I recently discovered you can press the windows key and just start typing the name of the application, this is really fast and effective, so I usually launch apps like this now.

Launching Applications.

2. No minimize button on the windows.
I don't miss the maximize button at all but I did miss the minimize button. I use that a lot to unclutter my workspace. I fixed this by installing the Gnome Tweak Tool and re-adding the minimize button.

3. Shutdown is only visible in the system menu once Alt is pressed.
This makes no sense to me, when finishing using my laptop I only suspend it approximately 20-30% of the time and of those times I usually just close the lid. I appreciate that simplifying the system menu is a good thing but this is just confusing and only caters for the workflow of a minority of users.

I CAN HAS POWER OFF?


4. Opening Multiple Instances of the same Application
It's kinda weird the way Gnome 3 handles this, if you go to applications and click on a program it will be launched if it isn't running or if it is already running will just take you to the running instance. I like to open several terminal windows at the same time, so instead to open more than one I have to use File -> Open Terminal from a running instance. When using Alt-Tab to switch between windows it also groups the same applications (see screenshot) but I quite like that personally.

Update 14/06/11: Ctrl-Click on an application will open another instance.

Alt-Tab with Application Grouping.


THE UGLY?

Ain't got nothing really, I'll likely be updating my desktop computer to Ubuntu 11.04 soon, so maybe Unity will prove better sport in this department.