Last week I lost several productive hours resetting my ‘insecure’ password on several websites due to a security breach at a website I haven’t used in more than a decade, if you’ve ever used that site, you’d be well advised to change your password pretty much everywhere. In order to prevent this happening in the future, I figured I’d write up a simple best practices article on handling passwords and authentication. There’s nothing particularly new here, but it’s always worthwhile revisiting the basics.
What Not to do?
Never store your users’ cleartext passwords in your database, no exceptions. The most important reasons is that if your site is compromised or your backup drive lost all your users will be in danger. The attacker will have access to a ready-made list of passwords and the related email addresses which they’ll be able to go off and cause havoc with other websites. The second risk is that any one of your staff members could steal that information and use it to attempt to access other systems where your users have used the same password.
While it’d be nice if users never reused a password, the reality is that nearly everyone does it. We need to be responsible and realistic, and take the necessary precautions to protect our users.
Instead what you should be storing is a hash of the user’s passwords. This will let you verify that the password provided matches the one on file, but will never let you recover that password.
But just storing a hash of the passwords isn’t enough, this would still leave you open to rainbow attacks where an attacker pre-calculates hashes of millions of passwords, then compares the hashes with the values they’ve stolen from your database. To prevent this you need to salt them. This means storing a small random value against each of your users and adding that to the password before you hash it.
Resistence to Brute Force
The last piece of the puzzle we have to solve is what happens when someone manages to get their hands on the password hash. As Coda Hale points out modern hardware can calculate upwards of half a billion hashes a second. This means that our hash has to be both one way, and slow. General purpose hashing algorithms like MD5 will not do.
Putting it Together.
Thankfully the excellent bcrypt-ruby gem provides a convinient wrappper around both a slow-secure hash function and a salt, saving us an awful lot of effort.
So this leaves us with a user model with a +hashed_password+ attribute, and a simple migration like:
1 2 3 4
We’ll also need two kinds of method on the model itself, hashing methods and verification methods. We’ll cover the hashing methods first:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
The next method we need to implement is the authentication method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Cleanse the Logs
Finally, you need to make sure that your user’s passwords don’t get logged either, thankfully this is built right in with rails.
1 2 3
All in all using secure password hashing takes around 15 lines of code so there’s no excuse for not doing it in your applications. It’s also relatively simple so I wouldn’t suggest using a plugin if all you want is simple secure login code, those plugins come with tonnes of additional features which you may not want.
Finally a product plug, 1Password from AgileWebSolutions is a really useful tool for generating, storing and recalling secure passwords for the myriad of websites which require logins. It’s much simpler and more secure than re-using some ‘insecure’ password on dozens of websites. It also has the side benefit of being pretty-well immune to phishing attacks.