Cookies are a convenient, often necessary way to maintain state and data in web applications. Since this is well known, cookies are a target and potential vulnerability you have to be aware of when developing for the web.
There are various ways to go about improving cookie security. In this article we’ll discuss an additional method, client-side encryption.
Attacks on cookies
Cookies are stored locally on a computer, usually in clear text. If a computer is accessed by multiple people, one person might scan another’s cookie folder and look for things like passwords or long-life session IDs.
Cookies are generally set server-side using the ‘Set-Cookie’ HTTP header and sent to the client. This makes them a target for network sniffing. You can use SSL/TLS to prevent this by encrypting the network packets, but many sites, such as Facebook, only use HTTPS during login, and then switch to standard unencrypted HTTP for ensuing requests. Tools like FireSheep make sniffing and hijacking session cookies (just another cookie!) trivial in certain conditions.
Encrypting cookies in your browser
The API works by combining a random, dynamically assigned ‘seed-key’ with a generally weaker user or application secret (like a password) to generate a strong 256-bit key. It then uses that key to encrypt and decrypt cookies on the client. It’s a very minimal, unobtrusive API by design, and should easily integrate with preexisting applications and frameworks.
Then, paste in the following code, replacing __YOUR_SUB_TOKEN__ with the sub-token you get from sign-up.
Now, you’re ready to read and write encrypted cookies. To save an encrypted cookie to disk make the following call.
setSecureCookie(secret, cookieName, cookieVal);
The ‘secret’ parameter is determined by your application. You may require the user to enter the secret. Or, you could generate it automatically from internal application values like username, timestamp, etc. You could even provide a truly random, i.e. actual, encryption key for ‘secret’. It’s entirely based on the security/convenience trade-off you decide on.
To decrypt and access the cookie value, make the following function call. Note that ‘secret’ in this call must be the same ‘secret’ used in setSecureCookie().
var cookieVal = getSecureCookie(secret, cookieName);
Consider the vulnerability scenarios outlined at the beginning of this article. In each case, the exploit only mattered the moment the attacker obtained the cookie’s value. With encrypted cookies, accessing the cookie does not mean accessing it’s value.
In the case where someone gains local access to a computer and scans for cookies, encrypted cookies prevent the attacker from viewing the cookie contents.
Let’s say you encrypt cookies and send them in their encrypted form to the server for storage. The server can later use ‘Set-Cookie’ and the cookie remains encrypted until it reaches your client. A hijacking attack is mitigated in this case, even if the cookies are sent over clear HTTP.
Xss exploits are more difficult to stop since the attacker may have full access to the page and its dynamic elements. If the attack is specific enough, it might simply access variables in memory. In the case where an Xss exploit accesses your cookies, however, the same protections as above apply. The encryption must be circumvented first.
It should be noted that encryption doesn’t prevent a malicious user or process from damaging cookie values and making them impossible to decrypt. This would put your application in an inconsistent state, but the value of the cookies themselves won’t have been compromised.
In addition to improving security, you can also use encrypted cookies to enhance user privacy. As more and more user data lives ‘in the cloud’, and concerns about privacy become more and more prominent, you may want to make assurances to the user that some, or perhaps all, of their data is known only to them.
One way this might be accomplished is to prompt the user for an additional secret after they’ve logged in, and use that to store sensitive information in an encrypted cookie. The problem with cookies, however, is that they expire. By storing them server-side in their encrypted form, and using the ‘Set-Cookie’ header, you can easily persist secret client-side data for longer, even indefinite periods.
An example of this would be an online check register where the transactions are stored online, but the actual bank account data is accessed through an encrypted cookie. By storing the cookie on the server and decrypting it locally, the bank information can be used within the context of the application yet remain private to the user.
Encrypting cookies on the client provides an additional level of security not just for the obvious reason, that it encrypts sensitive information before storage. It gives you another way in which to design security into your application. It’s not just a tool for security, either, but also a tool for privacy, both of which are important issues in modern web programming.
Great article, and I’m glad this kind of topic is being covered. There is something that I can’t quite get my head around though.
Firstly, I am no security expert so I apologise if there is anything fundamental missing from my understanding of this. You mention the cookie can be decrypted on the client side. Is this also possible on the server side, given the knowledge of the passphrase and the seed key? How would you get this data to the server in a secure manner?
I guess the thing I can’t get my head around is, if the purpose of storing the cookies in encrypted form is so that sensitive data is not revealed as plain-text then what purpose is there to be able to decrypt the cookie client side? As the very fact that the cookie can be decrypted means that the value of this encrypted cookie is vulnerable to being exposed (given that an attacker has the passphrase and seed key). Does this method simply make it harder for an attacker to read the value of a cookie at surface value?
Should this method only be used on the client-side? If you were to use this method to send data from the client to the server, would that make this method insecure because of the necessity to also send the passphrase and the seed key thus making it trivial to decrypt?
Hi Max, thanks for the questions. With this library, cookies are only encrypted and decrypted on the client-side. The cookie may be stored on the server to persist them over the long term, but they are still in the encrypted form. To get at the decrypted value, they must be sent again to the client. (There are some hacky ways around this I can think of, but if you’re going to do encryption server-side, my recommendation would be to use a different method, maybe in combination with this one.)
All the encryption in these cookies are effectively based on the ‘secret’ value. It’s the unknown quantity. ‘seed key’ is used for the key derivation function, but it’s stored with the cookie. You have to know ‘secret’ in order to decrypt the cookie.
The way the key is derived is an important part of security, and is based on PBKDF1, which at the 500-foot level just takes ‘secret’ + ‘seed key’ + a few other smaller values and hashes them 1000 times using SHA1.
Humans are notoriously bad at generating and retaining entropy. This just means we’re terrible random number generators. If you ask a user to type the ‘secret’, you’ll probably end up with his password, which may or may not be very good. So by applying it to PBKDF1 you’ve basically created a strong key from a really weak one.
By asking the user to type in ‘secret’ it’s reasonable to expect that only the user can decrypt the cookie, and therefore you have obtained user privacy in the context of the application. If ‘secret’ is something the user stores in his or her head, then just because an attacker may gain access to the computer or network, doesn’t mean they can easily decrypt the cookie. Apply additional methods of security, like SSL, etc, and you’ve further strengthened your application.
Thanks for sharing such a nice information..