Also commonly referred to as "permissive sessions", session fixation is a cracking technique that gets around the problem of predicting session IDs. You see, by default every session in PHP gets an ID that looks something like this: psu31m28jovm95mmo19a267fg4. It's a very long, complicated alphanumeric value that is generated randomly. What's more, the methods used to generate it - unless the cracker has physical access to your system - are above prediction. Therefore, there is no way a cracker can break into others' sessions.
However, an interesting feature of PHP is that it lets users tell it what session ID they are using. If PHP cannot find session data for that ID, it just creates session data afresh for it. Take a look at this link: <a href="http://www.yoursite.com?PHPSESSID=abc123">Click here!</a>
What that does is let people click through to a given site, passing in a specific PHPSESSID. PHP will then use this for the session ID for that user, and, what's more, the person who created that link doesn't have to bother trying to predict what the session ID is, because they already know: abc123.
This is the nature of session fixation: users being handed non-random session IDs that can be hijacked by others. Once a cracker knows a user's session ID, they can go in and view potentially private data: passwords, credit card information, etc. This is - as you can imagine - a bad thing.
How to solve session fixation? There are several ways:
When a real session is created, set a value in there such as IS_SAFE.
Change session IDs on privilege elevation
Store user-specific data in sessions that can be checked
The first option sounds like it might work, but ends up being no better. Yes, it does stop hackers plucking session IDs such as abc123 out of thin air, but it doesn't stop them going to your site, getting a real session ID, and using that . Essentially they create the session themselves, and use the valid session ID given back to them by your site to ensnare others.
The second option requires a little explanation. "Privilege elevation" is where users change from being an Internet visitor indistinguishable from anyone else to being someone with some sort of rights. The most common example of this is when people log-in to your website: they now have privileges, and so should have their session changed. Changing session ID isn't hard; in fact, it's just one call to session_regenerate_id(). Note that session_regenerate_id() takes no parameters and returns no meaningful value, it simply changes the session ID to a new, random one. In order to do this, it must send a cookie back to the client: this means you must use the function before any HTML content is sent, or enable output buffering.
Regenerating the session ID on privilege elevation works just fine: PHP automatically copies all the data from the old session to the new session, and leaves the old one intact too. This means that the legitimate, privilege-elevated user, gets to access all their data safely, while the cracker is looking at the stale file. Changing the session ID with session_regenerate_id() is very fast; I don't see any reason why you couldn't call it every page if you were really paranoid.
Moving on, storing user-specific data in the sessions so that you can see quite easily whether the current session user is the same as the user that created it is actually quite easy to do. It turns out the only data you can be sure to get from clients is a user agent (the browser name), but this is actually enough. Sure, 90%+ of people use Internet Explorer, but there are quite a few major versions of IE out there and many, many sub-versions. The IE installed on my Windows box gives the user agent "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 1.0.3705)", and yours probably gives something very different. So, we can store this in our session as a known identifier of our user. If someone comes along with the same session ID, we can check their user agent against our stored value, and take immediate action if they differ.
Author's Note: It is not advised to use the IP address of visitors as their unique identifier, because proxying ISPs such as AOL group IPs together: one IP address could be used for a million people, whereas one person might appear to have several IP addresses as they go through different proxies.
In action, there's no harm storing user-specific data and using session regeneration: the speed hit is very minimal, and you get a healthy boost in security.
If this was helpful, please take a moment to tell others about Hacking with PHP by tweeting about it!
Next chapter: Hosting PHP >>
Previous chapter: Pre-initialise important variables to safe values
Home: Table of Contents
Copyright ©2015 Paul Hudson. Follow me: @twostraws.