Before we begin, I need to mention a caveat. These are my recommendations and current understanding of best practices, but I want to be clear that I’m not a security expert. What I am is a developer, which means that I’m on the front lines of actually implementing security solutions. I’m always trying to stay on top of evolving threats and best practices so that I’m implementing security barriers instead of security holes! If you have better ideas or find flaws with mine, please share. It’s the only way we as developers will get better at security—and let’s face it we have a pretty bad track record.
A lot of security breaches you hear about involve loss of data, like financial information, but what’s even worse are breaches where clear text passwords are exposed. Sure this is bad for the site where the passwords originated, but it’s also a chain reaction of bad since most users use the same passwords amongst many sites. So if a cracker gets the usernames and passwords from CuteKittens.com, then suddenly they also have complete logins for FaceBook, Amazon.com, email systems, etc. Sure I blame the users for bad password practices, but this kind of thing is easy for developers to avoid.
It boils down to this: Never store users’ password! It’s not enough to only encrypt the passwords. You need to hash them.
What exactly is the difference? Both encryption and hash algorithms turn secrets into obscured bytes of data. Encrypted data can be decrypted with the right key, but there is no way to reverse a hash. A hash is a one-way algorithm. A hash algorithm also produces wildly different output with even small changes to the inputs. So “Hello” might hash to 0x261A, but “hello” might hash to 0x9FC0. MD5 is one example of a hash algorithm, though it has some flaws. Generally the SHA-1 family of algorithms is recommended instead.
So I’ve hashed my passwords and I’m now storing the output of the hash in my database instead of the plain text. I’m all good, right? Unfortunately not.
A hash cannot be reversed, but it can be broken. The way it’s broken is pretty simple. You pre-compute the hashes for every possible password ahead of time. Then you take the hash value, look it up in your table and viola, you have the plain text. This giant lookup, called a “Rainbow Table,” used to be beyond the capabilities of computers, both due to the length of time needed to calculate hashes and the massive storage requirements of the resulting output. You could hash tens of thousands of passwords, but it would take a week and fill up your hard drive. Now, with huge storage and massively parallel computing (including the math capabilities of high-end video cards), rainbow tables are easy to compute. Or you could be lazy and just download a multi-gigabyte rainbow table from the Internet. (I’m not kidding, I googled and found freerainbowtables.com.)
As simple as rainbow tables are, there’s an equally simple way to render them moot; add salt to the hash. A salt is a suitably long chunk of random bytes that are appended to the password before it’s hashed. Because the password has extra data, it will now hash to a different value. This effectively means that each random salt value will need its own giga-sized rainbow table.
At this point, with a salted and hashed password you are pretty safe, but there’s one additional step you can take to make your passwords harder to crack: add time. Instead of hashing once, run the hash output (plus the original salt) through your hash algorithm multiple times. This adds computational overhead, which means that any cracker trying to match hashes in your database will have to spend more time. I’d time your algorithm and then loop so that it takes from 0.5 seconds up to 1 to 2 seconds. Note that this will slow down your users’ logins as well, so don’t go too crazy.
This advice only pertains to the security of the passwords stored in your database. It doesn’t prevent crackers from gaining access to your systems. It just means that breaches have fewer negative impacts. Hashed passwords don’t alleviate the need for security in other areas.