This post was on the front-page of Dzone today: Bullet Proof Cookies. It got like 7 up votes when I saw it.  Did anyone actually read the article? This shows a horrible way to use cookies.  Well, maybe not horrible, but at least it is certainly over-kill.

The author recommends encrypting your cookie data, digitally signing it, and sending that to the browser. The idea is that by encrypting the data, the client won’t be able to know what it is or modify it. This is a nice way to go, but why go through the trouble when you don’t need to store data in cookies to begin with?

Specifically, my rule of thumb is that if you don’t want a client to know the data, don’t send it to them.

The most common feature to use cookies to store information is the “remember-me” feature of most web sites. Once a user successfully logs in, you want to allow them to be auto-logged in. The author of the above article suggests that you send them their userid, but since you don’t want them modifying the cookie, you encrypt it. Well, that’s one way to do it (the hard way).

There are two other methods that are easier (and still secure):

  • Send hashed data
  • Send a randomly generated ID that links to data on the server

Secure

I’ve not looked at the internals of WordPress, but based upon the cookies that I have on my system from wordpress.com, it looks like they implement the remember-me function by sending a string that looks like something this: username%hexadecimalsalt%hex-sha1?-salted-hashed-password. (I could be wrong about what WordPress does, but since this is how *nix /etc/passwd files work, it’s pretty secure anyway)

In this case, you don’t see the password, but you have the hash of the password. If you change the username, it won’t work. If you change the rest of the string, it won’t work. The only downside, is that if an attacker has enough time, they _could_ figure out what your password is using a brute-force approach. This technique is useful in situations where you are checking two values, and unsecured one, and a secured one (the username and the password). On the server side, you can use the browser’s cookie value to lookup the username, and compare the sent hash with a calculated one. But what should you do if you don’t have a unsecured data to perform a lookup? The answer is: use a randomly generated id.

More secure

I personally never store data in a cookie, but rather store a randomly generated identifier that maps to the data on the server-side. This is very similar to the way that sessions are implemented (PHP,Java, ASPX,etc). Let’s say you have a user that wants to be auto-logged in (remembered) with they visit your site the next time. What I do is to add a new field to my database’s user table named “rememberme”. Then I’ll generate a new, random string (UUID, SHA1 hash of some random value, etc…). I’ll then store that value in the database user user.rememberme, and send that value to the user as a cookie. This avoids storing data on the browser that could be exploited, and lets the user still have their remember-me functionality.

A bit more secure

Now, this approach is no more or less secure than standard cookie based sessions. That is to say, it can be easily hijacked if someone obtains the value of the cookie. One way to generate some sort of signature of the user’s browser using the User-agent, IP address, etc… This could be as simple as using only the IP address, or concatenating the User-agent string, IP address, the “Accept-*” headers, etc, and hashing the concatenated string. You could then store this browser signature along with the randomly generated id above. This would give you a little more data to use to authenticate your user and be more confident that it isn’t a man in the middle attack.

Paranoid level of security

Now, if you must store data in a cookie that the user isn’t supposed to know, or that you don’t want to store in plaintext (passwords), then yes, encrypting and signing the data will make it more secure.  But you’ll be adding a ton of overhead to your HTTP request, so be sure to restrict the domain of the cookie.  Also you’ll still not be able to verify that the client is who you think they are. To do this, you’ll need to combine one of the above methods with SSL-only cookies and the secure bit. And if you’re need this level of security, you probably shouldn’t be using HTTP anyway, you should be using HTTPS. (Actually, if you need this level of security, you already know all of this and shouldn’t be reading this!).

I hope this helps, and gives you a few more tools to use in your secure cookie arsenal without needing full-blown encryption.


  1. Vidar Hokstad

    I agree with most of this, but the “remember-me” value is in effect a temporary password, so that alone doesn’t make much difference compared to a hash of the password unless you regularly expire the remember-me values.

    I tend to also include a time element, so the cookie value might be something like this:

    sha1([epoch / whatever-number-of-seconds-to-remember-for] . $secret_server_key . $username . $key);

    Where key is either a special “remember-me” value like you use, or the password. If you use a long enough secret key, the the problem is computationally intractable unless your server is compromised, in which case the attacker don’t need the passwords, so avoiding using the password as part of the input to the hash algorithm doesn’t really buy you much.

    The one thing it does buy you if you’re _very_ strict about access rights from your frontends to the database is that you can invalidate all the “remember-me” values without having to reissue new passwords to all users. But frankly, if I had an intrusion to a machine taking user passwords in at any stage, I’d assume the worst and reissue passwords to everyone.

    Note that if you don’t want to store the password in plain text in your database, nothing stops you from using the same approach above with the hashed password instead of the plaintext, though it of course means that if someone gets hold of the hashed passwords _and_ the secret key used by your server _and_ figures out the combination of inputs to the hashing algorithm, they can use the hashed passwords pretty much like the real plain text password.

    Adding the time element also means that the key is worthless after a specific amount of time regardless whether the attacker got hold of it before it expires on the client.

    (Of course, always remember to make sure an attacker can’t set their own password just by virtue of being logged in)

  2. Steve Barham

    Actually, the linked article isn’t too far off a good implementation.

    The purpose of encrypting the data is not to prevent people from modifying it; that’s what signing the data achieves. Encrypting the data is to prevent people from reading what you have sent them. This is obfuscation, and shouldn’t be necessary (you should never leak sensitive information to the client), but there’s no overall harm in it.

    You might want to look at schemes such as HMAC [http://en.wikipedia.org/wiki/HMAC] - these are commonly used as a ‘token’ to hand back to the user on successful authentication. Typically this would contain the username, the time the token was issued, and the expiration date of the token.

    From that point forward, the client just needs to send its HMAC token to the server; the server can then verify that it issued the token by comparing the signature chunk of the token with the unsigned chunk, and then knows the identify of the user; it can also check that the token has not expired, etc.

    There is also the substantial benefit that authenticating requests doesn’t require a trip to the database once the token has been issued as all that is required is to validate the signature.

    This is hijackable by stealing the cookie, but that’s an innate fact when emulating a stateful session over a stateless protocol; bounding it with an expiry date, or with some other aspect of the client (remote IP?) would mitigate the problem.

  3. mbreese

    @Steve Barham
    I completely agree with you that the best scheme is token based, of which a signed token with an embedded expiration date is even better.

    What I wanted to point out in the linked article was that you shouldn’t need to send data to the client at all, just some sort of token.

    I hadn’t thought of using an HMAC token, but that would certainly be more secure.

  4. Paul Houle

    There’s one big difference between (1) storing a ‘key to data on the server’ and (2) decrypting or hashing the contents of a cookie.

    (1) is easily scalable, and (2) isn’t. You can accelerate the ability of your system to decrypt or hash data by adding more front-end servers. (1), on the other hand, depends on a central database server. You can shard it, but it’s more trouble.

    Keeping data in cookies in signed and/or encrypted form makes a lot of sense when you’ve got a small amount of data (up to a few hundred bytes.) If you’ve got 100k of state, you’ve got to keep it on the server.

  5. mbreese

    @Vidar
    I hadn’t thought of including a time component directly to the hashed values… and I’m not sure how that would make it possible to mass purge saved remember-me values.

    I’ve also gotten out of the the habit of using a server-specific secret key as salt when hashing passwords. I’m not entirely sure why, but I had a reason at the time. I’ve started storing the salt with the hash as my passwords, something like: sha1:salt:hashedpassword. Now, this certainly isn’t more secure because now someone also has the salt with which to try to crack the hashes. But the likelihood that someone could find your shared salt is pretty high if they’ve also gotten access to your database.

    Ultimately what I normally do is a little more elaborate than what I described above. I usually have a separate table in the database for remember-me type tokens. I’ll store the user-id, the token, and the date it is issued. This way, I can check that the token hasn’t expired (lets say 2 weeks), and belongs to the same. This also lets the user be ‘remembered’ on multiple machines w/o having their remember-me tokens be the same.

    This avoid needing to cryptographically sign a token (since it’s passed in the clear anyway, it can be stolen itself), and also lets you expire tokens independently from the client. If you want to get even more fancy, you could also generate a signature of the browser, but that’s probably overkill.

    I should also probably mention that the above schemes are for long-lasting tokens… for short-lived, but highly secure situations, everything should be tied directly to the client’s IP address as well. (Such as in single-sign on servers).

  6. mbreese

    @Paul
    Well, yes and no. :)

    You have to remember to use-case for this: the remember-me functionality of a site. In this case, the round-trip to the database should only occur if a) the user isn’t signed in (you’ve already loaded the session from disk, cache, or database) and b) they have passed in the “rememberme” cookie. In this case, a single round-trip to the database doesn’t cost you very much because you’d ideally be doing it only once per session.

    You’re right that if you put data into the cookie and encrypt it, you can save load on the database server. However, I don’t see why you’d ever want to do this.

    It is just as scalable to save data on the server side in a cache of some sort (like typical session-data). In order to make it easier to scale your web server load, you don’t have to store the session information on the server. You could store it in the database (causing at least one trip to the database on request), or in a distributed cache (like memcached). But it is just as easy to use sticky sessions.

    I’m also not sure how much overhead would be involved with the constant decrypting/encrypting of the cookies. Perhaps it would be negligible, but it certainly isn’t free.

    However, this starts to move the conversation to the realm of scaling web sites, which isn’t quite germane to this post (but probably a good topic for another day!).

  1. 1 Secure/bullet proof cookies - Under the Outside

    [...] seems to be a discussion about bullet proof cookies lately and I’m quite surprised that most web developers [...]



Leave a Comment