M1 had to suspend its iPhone 6 pre-orders last night because of the discovery of a security vulnerability in its pre-order system. The details shared by Channel News Asia (CNA) revealed that information of other customers could be leaked by trivially modifying a session cookie using a Google Chrome browser cookie plugin. It’s a silly amateurish mistake. Unfortunately, such mistakes are not rare.
Security is often an afterthought, slapped on just before an app rolls out into production. Sometimes it’s worse. There’s no due consideration given to security at all, not until some kind soul goes out of his or her way to point it out.
Session cookies is one significant area of trouble. This is a part of Broken Authentication and Session Management which consistently appears in OWASP’s top 10 web application vulnerabilities.
Many web programmers are using cookies wrongly, because they probably learnt things wrongly to begin with. All the “dummies guide” to using browser cookies are, for the sake of simplicity, giving examples that are naively insecure, and then many programmers are just blindly following the examples. There are some programmers who treat the cookies as simply an internal application variable. Well, they are totally not.
Many of the lessons here are similarly shared by OWASP’s Session Management Cheat Sheet.
- Any information you want to keep about a user, or their browser session, should ideally be stored entirely on the server side, accessed (or indexed) by a primary key that is a totally meaningless string of gibberish.
- That meaningless string of gibberish will be your session ID. It must be long enough. OWASP says 16 bytes. I say, bytes are cheap on today’s storage and network bandwidth. Surely 32 bytes is not an issue, except of course for the bad guys.
- That meaningless string of gibberish needs to be random. As random as you can make them to be. For goodness sake MySQL’s AUTO_INCREMENT is… erm, anyone who uses that for a session ID should be banned from programming for life. Session IDs are the key. If someone can guess a workable key, then they can unlock something. Spend some time to learn about producing a cryptographically secure random hash. (This appears to be the problem with the M1’s vulnerability.)
- Avoid using default cookie names for holding the session ID. It may leak information about the programming language or application framework that you use. Of course, I’m not saying you should depend on obscurity as your method of security. We are just trying to make life tougher for the bad fellows.
- As mentioned already, your session ID is like a key. Make sure you help the user to protect it. Don’t pass session IDs in cleartext. In other words, use HTTPS. Make sure under no circumstances are session IDs exposed on HTTP. Mark the cookie “secure”, so that browsers will not transmit such cookies in cleartext HTTP.
- Better yet, use a different domain name entirely for content that is secure, which will use sessions, or which will have to deal with personal or sensitive information. Make the entire site HTTPS only, with no HTTP listener. Don’t even think about an auto-redirect from HTTP to HTTPS.
- Mark the cookie “HttpOnly”, so that browsers will not let scripts access the cookies. This will give a layer of protection against XSS attacks.
- You may want to set a cookie expiry, but you had better not depend on the browser to expire the cookie. Expires your sessions yourself, in your own backend.
- Remember to treat cookies data as untrusted input just like any other input you receive from a browser.
Sometimes there are valid reasons to bend some rules. Sure, but you had better understand what you’re doing and decide if the security issues have been properly addressed, and the risks are sufficiently mitigated.
One could write a whole book, or a whole website (which is what OWASP has done), about web application security. There is no way I can scratch the surface in a blog post. I do hope this will raise some awareness that cookie management is not something so trivial.