Sessions are, in many bits of web software, used to store the details of the currently logged in user, and to provide the concept of a user being logged in and logged out. Known is no exception in this.

These sessions need to be stored, in some way, on the server. Usually this is either stored in files, or in a database.

Known currently has the ability to store sessions in the currently enabled database engine (Mysql, mongo etc), or in files. However, for various reasons (e.g. you want to use a different database engine to store sessions, or you want to implement a fancy session store), it would be useful to decouple the sessions from the database.

This pull request adds the ability to specify, in your config.ini, a handler class implementing the Idno\Common\SessionStorageInterface interface.

This new class can then implement a session handler which uses a different storage engine, other than just files and the current database.

So, it’s been a frustrating few days debugging a supposedly simple single sign-on handshake conducted over SAML.

Further to my last post, here are a couple of gotchas that tripped me up.

Watch your session settings

If you’re using sessions, you need to make damn sure your cookie settings are the same in both your app and SimpleSAML’s config.php.

Sadly, this isn’t always possible, at least not without making an offering to the Elder Gods. SimpleSAMLPHP’s settings are fiddly, and in the time I was poking at it, I couldn’t find a way of getting it to entirely match the application’s more enhanced security settings (we, for example, stipulate various ini flags and up the session’s hash algorithm).

SimpleSAMLPHP also seems to have a habit of generating its own session ids, although I might have been blinking at the source too long.

Either way, I ended up commenting out the session initialisation code in SessionHandlerPHP::__construct() and replacing every instance of the session starting code with a call to the app’s session initialisation code.

This adds some maintenance debt, but life is too short.

Debug in incognito mode

If you’ve been banging your head against session problems for long, you’ll have a lot of cruft in your cookie jar.

A hard learnt lesson (obvious in hindsight) was that even if the code works, it’ll likely fail with our old friend Exception: The POST data we should restore was lost.

The simplest way of ensuring you’re going to be clicking through with a fresh session is to use your browser’s incognito mode to test, and after each test shut down all of these windows (they share a context, so you’ve got to close all tabs and windows to fully clear the context) and open a new one.

Hopefully this might save you some time and frustration.

Those of you who have done any amount of web application programming should all be familiar with the concept of a Session, but for everyone else, a Session is a way of preserving state between each browser page load in the inherently stateless web environment.

This is required for, among other things, implementing the concept of a logged in user!

In PHP, this is typically implemented by setting a session ID to identify a user’s browser and saving it as a client side cookie. This cookie is then presented to the server alongside every page request, and the server uses this ID to pull the current session information from the server side session store and populate the $_SESSION variable.

This session data must, obviously, be stored somewhere, and typically this is on the server, either in a regular file (default in PHP) or in a database. This is pretty secure, and works well in traditional single web server environments. However, a server side session store presents problems when operating in modern load balanced environments, like Amazon Web Services, where requests for a web page are handled by a pool of servers.

The reason is simple… in a load balanced environment, a request for the first page may be handled by one server, but the second page request may be handled by an entirely different server. Obviously, if the sessions are stored locally on one server, then that information will not be available to other servers in the pool.

There are a number of different solutions to this problem. One typical solution is to store sessions in a location which is available to all machines in the pool, either a shared file store or in a shared database. Another technique commonly used is to make the load balancer “session aware”, and configure it to address all requests from a given client to the same physical machine for the duration of the session, but this may limit how well the load balancer can perform.

What about storing sessions on the client?

Perhaps a better way to handle this problem would be to store session data on the client browser itself? That way it could send the session data along with any request, and it wouldn’t matter which server in the pool handled the request.

Obviously, this presents some issues, first of which being security. If session data is stored on the client, then it could conceivably be edited by the user. If the session contains a user ID, and this isn’t protected, then this could let a user pretend to be another. Any data stored on the client browser must therefore be protected, typically this is done by the use of strong encryption.

So, to begin with we need to replace the default PHP session handler with one of our own, which will handle the encryption and cookie management. The full code can be found on GitHub, via the link below, but the main bits of the code to pay attention to are saving the session:

// Encrypt session
$encrypted_data = base64_encode(mcrypt_encrypt(MCRYPT_BLOWFISH, self::$key, $session_data, MCRYPT_MODE_CBC, self::$iv));

// Save in cookie using cookie defaults
setcookie($session_id, $encrypted_data);

Followed by reading the session back in:

return mcrypt_decrypt(MCRYPT_BLOWFISH, self::$key, base64_decode($_COOKIE[$session_id]), MCRYPT_MODE_CBC, self::$iv );

If things are working correctly, you should see something similar to this:

Cookie Sessions

Limitations

There are some limitations to this technique that you should be aware of before trying to use it.

Firstly, the total size of all cookies varies from browser to browser, but is limited to around 4K. Remember, this is the total size of all cookies, not just the encrypted session, so your application should only store a bare minimum in the session.

Secondly, and this relates somewhat to the previous point, since the session is sent in the HTTP header on every request, you could eat up bandwidth if you start storing lots in the session. Another reason to store the absolute minimum!

Thirdly, session saving is done using a cookie, so you must be done with sessions, and call session_write_close(); explicitly, before you send any non-header output to the browser.

Have fun!

» Visit the project on Github…