Storing encrypted session information in a cookie

cookie

Our session system is due for an upgrade. Currently all PHP sessions are stored in the database, and some things are getting a bit slow. There have been a couple of approaches I've been considering, one of which is simply storing all the information in a browser cookie.

First I want to make clear I don't necessarily condone this. The reason I'm writing this post, is because I'm hoping for some more community feedback. Is this a really bad idea? I would love to know.

The benefits

If all the session data is stored in the browser, it means that I don't need to store it on the server. I actually don't care all that much for having the data on the server (unless it's the only secure way), it's mostly a gigantic map with session tokens and user id's (along with some other info).

I also feel it's more natural for HTTP, as it makes it a bit more stateless.

Sample code

  1. <?php
  2.  
  3. class BrowserSession {
  4.  
  5. public $secret = 'this will need to be a cryptographic random number';
  6. public $currentUser = null;
  7.  
  8. // Sessions time out after 10 minutes
  9. public $timeout = 600;
  10.  
  11. function init() {
  12.  
  13. if (!isset($_COOKIE['MYSESSION'])) {
  14. echo "No session cookie found\n";
  15. return;
  16. }
  17.  
  18. list($userId, $time, $signature) = explode(':',$_COOKIE['MYSESSION']);
  19.  
  20. // The cookie is old
  21. if ($time> time() + $this->timeout) {
  22. echo "The session cookie timed out\n";
  23. }
  24.  
  25. if ($signature !== $this->generateSignature($userId,$time)) {
  26. echo "The secret was incorrect\n";
  27. }
  28.  
  29. $this->currentUser = $userId;
  30.  
  31. echo "Logged in as user: $userId\n";
  32.  
  33. }
  34.  
  35. function login($userId) {
  36.  
  37. $this->userId = $userId;
  38.  
  39. $time = time();
  40.  
  41. $cookie = $this->userId . ':' . time() . ':' . $this->generateSignature($userId,$time);
  42.  
  43. setcookie('MYSESSION',$cookie,$time+$this->timeout,null,null,null,true);
  44.  
  45. echo "Set cookie: $cookie\n";
  46.  
  47. }
  48.  
  49. function generateSignature($userId,$time) {
  50.  
  51. $stringToSign =
  52. $userId . "\n" .
  53. $time . "\n" .
  54. $_SERVER['HTTP_USER_AGENT'] . "\n" .
  55. $_SERVER['REMOTE_ADDR'];
  56.  
  57. return hash_hmac('SHA1',$stringToSign,$this->secret);
  58.  
  59. }
  60.  
  61. }
  62.  
  63. ob_start();
  64. $session = new BrowserSession();
  65. $session->init();
  66.  
  67. if (isset($_GET['login'])) $session->login($_GET['login']);
  68. else {
  69.  
  70. echo '<br /><a href="?login=1234">Log in as user 1234</a>';
  71.  
  72. }
  73. ?>

A few notes:

  • The preceeding code was just intended as a proof of concept, it's missing some validation.
  • Currently the secret would be the same for every user. I was thinking of appending some per-user information to the secret. If somebody does guess or bruteforce the secret, they would only have access to a single users' information.
  • If a user changes their password, existing sessions should expire. To do this the signature should also include a sequence number that changes when the password changes.
  • Currently this only stores a user id. It could be extended to contain more data, but this is all I need.

So, is there anything fundamentally wrong with this approach? In general the client should never be trusted, but for setups where the security requirements aren't as high (highly subjective, I know) I feel this might be strong enough. OAuth, OpenID and Amazon AWS all seem to trust HMAC+SHA1, but those applications do work differently.

Credit where it's due

I first asked this question on stack overflow. The users there already gave some great suggestions and pointed out some of the flaws. Thank you!

Sharing sessions between html and flash

cookieThis has been an issue that has been driving me pretty crazy.. I can't seem to find out how to share a (cookie-)session between flash and php.

The problem is that in certain situations Flash ignores session cookies when sending requests. The situations I know of are Flash Uploads and using Flash Remoting in internet explorer.

I asked my question on #webappsec and on the web application security mailing list, but there wasn't really somebody who could answer my quesion..

Options

  1. I can pass the session id using flashvars directly. Problem with this is, is that the session id is directly embedded into the html and can therefore be stolen using CSRF.
  2. I can use a temporary token, but anybody who has this token can do everything the user can in the flash application. For just the uploads it can work, but for everything else its not really flexible, and doesn't really fix the problem.
  3. I could turn off httponly cookies and pass the session id using javascript straight to the flash movie.. This could be me only option, but I dislike it because its not as transparent as it should be and requires additional logic using javascript and flash (and php).
  4. Force the user to login when using flash.. Not really a nice solution from a usuability perspective..

I'm wondering how other people go about this.. Is there a satisfying solution at all? Or can it only be done using a combination of nasty hacks?

 1

About

My name is Evert, and I've been writing semi-regularly on this blog since 2006.

I'm currently available for contract work.

more info.

Subscribe

Dropbox

Dropbox is a simple cross-platform online backup and sync application. The first 2GB of space is free, and both you and me get an extra 250MB extra space if you sign up through this link.