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.
1 2 3
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.