Far too frequently, systems are hacked and their user databases are compromised. And there are far too many cases where the database contains plain text passwords, poorly hashed passwords, or two-way encrypted passwords, despite the wealth of resources available on how to properly store user credentials. And it's not just legacy databases; just this week, I saw a reddit thread with at least one developer advocating custom hashing functions and "security through obscurity".
Though cryptography is a complex subject, you don't need to be an expert to be a good steward of your users' credentials. Just follow these simple rules when selecting a scheme:1
- Use a proven one-way hashing algorithm that has been publicly reviewed by security experts, such as bcrypt, PBKDF2, or scrypt.
- Don't attempt to write your own hashing algorithm. You can't rely on "security by obscurity"; you must assume the attacker has your source code, and can attack your inferior algorithm.2
- I mean it, don't write your own hashing algorithm! Just use one of the ones from Rule #1. They are free, available on virtually any platform, and have been proven secure by the best minds in security.
- Seriously, why are you even still reading this? Rule #1 is all you need!
- Alright, if you are really going to ignore Rule #1, please at least notify your users that you are not safely storing their passwords, and in the event of a database breach, their passwords will become public knowledge.
I don't like to deal in absolutes, particularly in software development, but I truly feel strongly about this. There is no situation in which you should be developing your own password hashing algorithm!3 Here's an example of using PBKDF2 in Java without importing any external libraries; if Node.js is your thing, there's a module for bcrypt; there's a Ruby gem for scrypt; hell, even PHP has a built-in password hashing function that uses bcrypt. Really, virtually any language has a freely available library for any of those three algorithms. Just Google "your-language-of-choice bcrypt OR scrypt OR pbkdf2" and you'll find plenty of options.
Any hash function that has not been through rigorous public peer review is almost certainly not properly designed. If Niels Provos himself — one of the creators of bcrypt — was on my team and wanted to use a new hash function he created in private, I would pass on it and use bcrypt. Although I doubt Mr. Provos would be foolish enough to suggest using such an algorithm; he may have created the next great password hashing algorithm, but he would likely realize that it could not be relied upon until it had been evaluated by his fellow security experts.
So when you need to store user credentials, just please, please hash them with bcrypt, scrypt, or PBKDF2. It's easy. Don't try to be clever.
1 Even better: don't store user credentials if you don't have to. Consider using an OAuth provider, or your organization's internal identity system. It saves some effort, and saves your users from remembering yet another set of credentials.↩
2 This is essentially Kerckhoff's principle: "The system must not require secrecy and can be stolen by the enemy without causing trouble"↩
3 Okay, I suppose if you are entering something like the Password Hashing Competition or doing academic research, that's fine. But you still shouldn't be using it in a real application until it has been through thorough public review.↩
This is good advice when designing new systems, but what about legacy applications? Most of the time, programmers in industry aren't going to be designing new applications that store user credentials, they're going to be maintaining existing ones that were developed years ago. If you inherit a legacy application with a poorly-chosen password scheme, you've got four choices:
ReplyDelete1) Leave it and hope you don't get compromised.
2) Replace the password storage code entirely and delete all the old hashes, which will require forcing all users to reset their password. Getting management approval for this can be difficult, if not impossible.
3) Somehow convince management to hire a cryptographer. Good luck with this one.
4) Use key stretching techniques to strengthen the existing hashes, e.g. by compositing hash functions. This removes the need to have all users reset their password, but it usually falls under "designing your own crypto".
If you can't get management approval for 2 or 3, then what do you do?
If all the passwords are stored in plain text, you can simply re-hash them all at once; nice and easy.
DeleteWhen they are hashed using an inferior method (like MD5), I've done a phased approach, and it's actually fairly straightforward. You need to somehow identify users that were hashed using the older method: a 'hashtype' column, or the user has a null 'salt' column, whatever. Then when a user logs in, if their password is stored using the old method, the first step is to authenticate them. If authentication succeeds, you still have the plain text password they entered, so you can now hash it using bcrypt/scrypt/PBKDF2 and store it that way.
So over time, as users log in to your system, you gradually transition everyone over to the stronger hash. It'd be ideal not to leave the inferior hashes around that long, but I think it's the best option.