Friday, January 9, 2009

When and How to Use Cookies

(Disclaimer: The methods described below should be viewed as education purposes only! These methods are not certified by me to stop or prevent attacks to your site. Instead, this blog tries to explain some ideas for properly using cookies. In other words, use these methods at your own risk, but don't come looking to sue me if you or your site are compromised).

Cookies, by some, are one of God's greatest inventions for the Internet. To others, cookies represent all that is bad and evil in the world today.

In reality, they are both.

These little files are only as good or bad as you, the developer, use them. With the growing popularity in XSS Attacks and CSRF Attacks, there is no wonder developers are having trouble relating anything good with using cookies, and also why even more users are refusing to accept them.

In this blog entry, I'll explain to you how to make using cookies on your site safer by explaining the following:
When to use cookies, and when not to use cookies
How to properly validate users with cookies
Alternatives to using client-side cookies
References/Further Reading

When to use cookies
Cookies, by many sites in the past and, unfortunately, the present, are mainly used to identify a user who has access to a website. They are intended to be a beneficial file(s) that grants users a more pleasant experience while they are browsing, shopping, or later return to a site, without the need to retype their credentials. However, the same helpful intentions of cookies such as these are continually being turned into unpleasant security vulnerabilities for users and websites alike.

We first start with when not to use cookies.

Cookies should not be used to store sensitive information. To be more blunt, you shouldn't store unencrypted (or plain-text) values for usernames, credit card information, first/last names, addresses, phone/fax/mobile numbers, passwords, email addresses, websites, social security get the drift. Nothing personally related to your user nor anything that will be used to automatically authenticate/login a user should be available in a cookie.

While select few will scream “blasphemy” at such an idea, I assure you it is not I who is blaspheming. It is a common misconception for developers to think cookies are a safe warehouse for information. Such carelessness is leading the current craze of XSS and CSRF attacks among crackers (poorly and misleadingly labeled hackers) to be legit methods to obtain or attack a user/website.

So what should cookies be used for then?

Cookies should be used for simple and harmless storage, and/or by validating that an actual user is submitting a form from within your website (for examples using the form method, please see Preventing CSRF andXSRF Attack link below).

Some Ideas of simple storage:
Keeping track of a shopping cart of an unregistered user (registered users have the luxury of that information being stored in a database).

Remembering which test/quiz/questionnaire/poll a user has already completed.. Of course, I do not mean for use with prize-based poll/questionnaire/etc. These should be limited to registered users and, like before, stored in a database.

Tracking when a user is logged into your site to assist and report “Number of active/Logged In Users” status.

All of these methods are simple, and harmless uses to store cookie data. But like any other data coming from a user to your site, the above must always be validated. You must be as paranoid as possible when dealing with data coming from your client, and validate everything, always. Assuming that no one cares about your website, or that it's unlikely a cracker would ever want to take over your website is exactly why a cracker would choose to take over your site. The majority of crackers do this in order to obtain popularity within their group (or bragging rights if you will), and when one finds out it's possible, the majority of the others will follow in order to gain the same bragging rights. All courtesy of your website's vulnerabilities.

In general remember to just keep it simple, keep it light, and make values you store in a cookie obfuscated as well. If you want to store the ID of an item, name the cookie “session_id_for_user” and the ID the item_id multiplied by some integer and combination of letters/numbers.

Assume you sale Bourbon, and the George T. Stagg has an item_id of 15. The value you should store in the cookie should be b375gts. In other words, b(for bourbon), 375 (which is 15 x 25), and gts (the initials for George T Stagg).

Don't go using my example though. Be creative! Be obtrusive! The harder it is for a cracker to understand just exactly what your cookie values do, the harder it's going to be for them to realize that it's a worthless cookie to begin with.

How to properly validate users with cookies
One of the most popular things a developer wants to do anymore with cookies is allow for a returning user to have a more pleasant experience on their site by not needing to enter as much data to gain access. But since we've already stated that storing information used to authenticate a user is not only dangerous, but shouldn't be used, how should we go about granting users some sort of relief?

The answer, just like above, is an obfuscated encryption key that is used to lookup a user_id in a database, but requires the user to type in their password. Once the encryption key is used and the correct password entered, the current encrypted key is discarded, and a new one is generated.

While this may sound like it contradicts my statements earlier, it is in fact a legitimate method! How can this be? Well, first of all an encrypted hash generated from a combination of the user's browser agent (ie: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv: Gecko/2008121622 Ubuntu/8.04 (hardy) Firefox/3.0.5), a current timestamp (12388445), parts of the person's username and email address (smashed123 becomes maed23 and becomes omilom), all encrypted with SHA or MD5 (although there are numerous rumors about MD5 quickly becoming an insecure encryption). Now you have a nice, simple, and (for now) safe encryption. One that, even if decrypted, would be meaningless/useless to the cracker who attempts to hijack it.

And since we are still requiring the user to type in their password to the site in order to accurately authenticate them further, off-site attacks are even more less likely.

Even after all of this, I still do not use cookies for validating a user's login credentials. Unfortunately, I would rather suffer from losing a lazy user to giving an attacker even 1 iota of an advantage, no matter how good the methods appears.

Alternatives to using client-side cookies
There are of course alternatives to using cookies. Since I am more of a PHP fan, I will use PHP's server-side sessions as an example.

PHP stores sessions 3 ways: via cookies on the client site (which, if used, should be used in conjunction with the session.cookie_httponly setting to help reduce XSS vulnerability), on the filesystem of the server (usually located in /tmp with a hash value), or in the server's memory (not covered or recommended).

Since we're only looking at server-side methods, obviously that leaves server-side-based cookies. While this method is much more secure and reliable, it is still strongly recommended to follow the above guidelines when determining what sort of data to store inside of a session-based cookie.

Resources/Further Reading
Preventing CSRF and XSRF Attacks
The Felten and Zeller Paper (PDF)
Cross-site Scripting Prevention/Awareness

In conclusion, I hope this has been helpful in some way to anyone interested in security. While some of the above almost certainly can be improved on (and I truly hope the development community tries to assist in doing just that), I welcome any and all comments.

1 comment: