Wandering Thoughts archives

2011-08-08

You need to hash web app session IDs

Up until very recently (in fact, today), I could confidently recite what I thought was the best way to handle login sessions in web apps: every time someone logs in, you pick a large random number to be their session ID, give them a cookie with the session ID, and then store all of the details in your database under the session ID. When they come back, you get the session ID cookie, look it up in the database, make sure it's (still) valid, and go.

WRONG.

This approach is dangerously incomplete, as I discovered by reading a stackoverflow thread on web authentication (via Hacker News). To be secure you need to always store your session IDs in your database in some cryptographically hashed form, never in plain text.

To see why, suppose an attacker gets surreptitious read access to your database and you use plaintext session IDs. Of course you follow best practices and salt and hash your user passwords (using something like bcrypt), so that they are not feasibly recoverable. However, the attacker has all of the session IDs for all active sessions. While those sessions remain valid, the attacker can hijack any of them (by creating their own valid session cookie for the session) and thus do anything in your app as any currently logged in user that doesn't require people to reauthenticate. Depending on your application this may be quite a lot, especially if the attacker gets the sessions of administrative users.

(This is one reason why you should require the account's current password in your password change form, something that I hadn't quite realized until I started thinking about it now.)

On a side note, this applies just as much to sessions that use session cookies as it does for persistent cookies (contrary to what the stackoverflow writeup implies), because an attacker can use valid session cookies just as easily as they can use valid persistent cookies. What matters is whether they can create appropriate cookies given a session ID (you should assume yes) and whether that session is still valid, not what form the cookie takes and what options you've set in your official cookies.

As mentioned, to solve this you need to store the session ID only in some hashed form, just as if it was a password (because in fact it is a somewhat limited password). I can see a number of approaches to doing this.

The simplest change is to hash all session IDs with the same global value, which means that an attacker cannot directly recover a session ID given database access (you should assume that they recover your global value along with your database). This reduces the attacker back to the birthday paradox attack on session IDs, although they can do this offline and in bulk; you should thus pick a slow hash function, like bcrypt, and make sure that you are still using a suitably large and random session ID. The advantage is that you need no cookie or (significant) database changes; you simply hash the session ID before recording it in the database, and then hash it again before looking it up.

The better change is to treat session IDs like passwords and salt them individually. However, this means that you need some way to recover the session ID record given information in the cookie so that you can find the right salt. I can think of two approaches. First, you can store some index to the user record in the cookie, then have a way of recovering all sessions records for a given user; this gives you a feasibly small number of sessions to check (by taking each session's salt, hashing the salt plus the session ID from the cookie, and checking to see if it matches the session record's hash). The other approach is to directly store a key to the session record itself in the cookie. In effect, what you have done is change the problem; your hashed 'session ID' is in fact a session ID validator, and the key is the real session ID (which you might as well make a full sized random number of however many bits).

If I was doing this in a web app, my preference would be for the second approach because it leaks less information into the cookie. The first approach necessarily puts some sort of information about the user there, which might be useful for attackers or eavesdroppers.

(I'm sure that all of this is well known in the web app security community; I just feel like writing it down so that it sinks into my head. See the stackoverflow thread for more.)

PS: I maintain that you can't solve this by signing your session cookies with HMAC and a server-side secret; you should assume that your server-side secret can be compromised just as your database was. And if you are going to believe in a server-side secret, you might as well use the 'hashed with a global value' approach to storing session IDs with the server-side secret as the global value; you're just as well off either way if the attacker compromises only the database, and you're better off if the attacker compromises both the database and the server-side secret.

web/HashYourSessionIDs written at 22:52:10; Add Comment

An incomplete list of the ways around MAC address blocking

In an earlier entry I wrote that there were plenty of ways for someone with a banned MAC address to get back on your network. Since some people may doubt that, today I feel like running down some of those ways to emphasize how weak MAC-based blocking is.

The straightforward workaround is to change your MAC address to some new random one you made up (often you can just vary the last octet of the MAC address slightly), then register your 'new' machine for network access. This works best in environments with an automated registration portal.

The next option is to skip the whole registration process by borrowing someone else's MAC address, ideally the MAC address of a machine that itself is not currently on the network. This usually requires some advance planning to acquire the MAC addresses of other machines, but has the obvious advantage of working even if you can't register new machines.

The more extreme option is to skip straight to what you actually care about, which is getting the DHCP server to give you an IP address. Well, who needs a DHCP server? After all, if you know the IP address range and other routing information, you can just give yourself an IP address without bothering the DHCP server. (You probably want to give yourself a different IP address than you used to be using.)

It's quite difficult to stop the first two attacks without side effects. In fact I think it's close to impossible to reliably block MAC address impersonation if you allow machines to roam from port to port. It's possible to block the third attack but it requires that your DHCP server and your network firewall talk to each other, so that the firewall only passes specific IP addresses that the DHCP server has given out.

All of this leads to the larger point, which is that both MAC addresses and IP addresses are only a very weak form of access control. They will keep ordinary people out, but they're not going to stop someone who knows what they're doing. If you need strong network access restrictions, you need strong authentication either of machines, via mechanisms such as IPSec, or of users, via mechanisms such as VPNs.

(This is nothing new to networking people, of course.)

tech/AvoidingMACBlocks written at 00:48:45; Add Comment

These are my WanderingThoughts
(About the blog)

Full index of entries
Recent comments

This is part of CSpace, and is written by ChrisSiebenmann.
Twitter: @thatcks

* * *

Categories: links, linux, programming, python, snark, solaris, spam, sysadmin, tech, unix, web

This is a DWiki.
GettingAround
(Help)

Search:
By day for August 2011: 2 3 4 5 6 7 8 10 11 12 14 15 16 17 18 19 20 21 22 23 24 25 26 28 29 31; before August; after August.

Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.