On a recent episode of The Dev Show Dan and I talked about passwords. In particular, the topic of password hashing came up. I’d like to say up front that I’m not a security guy and most definitely not a cryptographer. However, I don’t have to be because there are much smarter people who have already done a lot of work on encryption schemes and have done it much better than I ever could.
This should go without saying: you shouldn’t be storing your passwords in plain text in your database. Unless you need to be able to retrieve the password later, it should be stored in the database in a hashed format. Thomas Ptacek, a very highly respected security professional, explains all you need to know about passwords in this blog post. I’ll save you the trouble of reading the whole thing: just use bcrypt as your encryption scheme. It’s the slowest to generate the encrypted hash. By virtue of being slow to generate, it would also take a very long time to perform a successful lookup using rainbow tables. See that blog post linked for much more information and a thorough explanation.
Just how much longer does it take to generate? The following is a quick ruby program I whipped up to benchmark. It uses each encryption scheme to generate a password 50 times. The following was how long it took to run on my macbook using ruby 1.9.1-p378. You can grab the script here if you’d like to run it locally. It contains absolutely no tests which makes my inner Corey Haines frown:
Password to hash: password user system total real MD5 0.000000 0.000000 0.000000 ( 0.001443) SHA1 0.000000 0.000000 0.000000 ( 0.001679) SHA256 0.000000 0.000000 0.000000 ( 0.001308) bcrypt (3) 0.080000 0.000000 0.080000 ( 0.086532) bcrypt (10) 4.550000 0.010000 4.560000 ( 4.601996)
The differences between the (3) and (10) are the "cost" of generating the password. The documentation for the bcrypt gem summarizes that very well:
Takes an optional :cost option, which is a logarithmic variable which determines how computational expensive the hash is to calculate (a :cost of 4 is twice as much work as a :cost of 3). The higher the :cost the harder it becomes for attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check usersâ€™ passwords.
But I’m getting off topic. The reason I wanted to write this post was to create a list of popular open source software and see what kind of passwords hashing schemes are in use. Here’s the list I’ve compiled so far:
Encryption Scheme: SHA1, MD5, or crypt
Notes: Previous Django versions, such as 0.90, used simple MD5 hashes without password salts. For backwards compatibility, those are still supported; they’ll be converted automatically to the new style the first time check_password() works correctly for a given user. More info:
Encryption Scheme: Double SHA1
Encryption Scheme: PHPass
Notes The awkwardly named PHPass library defaults to bcrypt (awesome) and falls back to DES or MD5 based salted hashes depending on the php version and supported features.
Encryption Scheme: SHA1
Encryption Scheme: MD5
Encryption Scheme: Proprietary hash method using /dev/urandom and md5
Encryption Scheme: SHA1
Notes: This was the defacto standard for a long time in the Rails world as far as authentication goes. Changing the encryption scheme in an application would be a relatively painless process.
Encryption Scheme: bcrypt, aes256, md5, sha1, sha256, sha512
Notes: This is configurable to any of the listed options. Default is SHA512. The author doesn’t recommend using MD5 or SHA1 in the README but provides the options for migration and compaitiblity. How awesome is that?
Encryption Scheme: MD5 by default
Notes: Christefano points out in the comments that MD5 is used by default but PHPass and AES are available via third party modules.
If you don’t see your favorite software here, either leave it in the comments or contact me and I’ll add it to the list. These are in no particular order, so I’m not trying to favor anything in particular (though we all know I’m mostly a Ruby developer).