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.

Above: discussion of best practices in passwords.
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:
-
Django
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:
http://docs.djangoproject.com/en/dev/topics/auth/
http://docs.python.org/library/crypt.html -
MySQL
Encryption Scheme: Double SHA1 -
WordPress
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. -
Expression Engine
Encryption Scheme: SHA1 -
Joomla
Encryption Scheme: MD5 -
phpBB
Encryption Scheme: Proprietary hash method using /dev/urandom and md5 -
ASP.Net Authentication
Encryption Scheme: Uses a concept of "providers".
Notes: There’s a BCrypt open source option available. -
Rails: restful-authentication
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. -
Rails: Authlogic
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? -
Drupal
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).
The notion of using bcrypt because it's slow is a little disingenuous, I think. Yes, it's going to take a while to brute force it, but you can effectively bypass rainbow tables by just salting your passwords before hashing them. I use two very long salts (one shared, one user-specific) per password, resulting in a very long string being used as the source input; something like that being crackable with rainbow tables is effectively nil.
If you're salting your passwords, using an intentionally-slow hashing algorithm just slows down your application, and potentially leaves you open to a nasty DOS attack, wherein an attacker would just spam your login action with passwords, putting undue strain on your server as it struggles to hash input passwords to compare against stored values.
There is something to be said for using bcrypt as a defense against passwords being extracted in the event that your database and code are both stolen, but you arguably have bigger problems if that happens. :)
wherein an attacker would just spam your login action with passwords, putting undue strain on your server as it struggles to hash input passwords to compare against stored values
There are various ready-to-use tools to thwart these kind of attack, fail2ban came on top of my mind. Here it is :
http://www.fail2ban.org/wiki/index.php/Apache
There is something to be said for using bcrypt as a defense against passwords being extracted in the event that your database and code are both stolen, but you arguably have bigger problems if that happen
This is a valid issue in certain scenarios, example: webhosting companies.
Customers may insist on getting shell account, and as they said, the customers are king. From there, it’s all game.
So yes, a stronger password hash, like bcrypt, is always welcome.
I suggest giving the article I linked by Thomas Ptacek a read through. It goes over in much more detail the reasons, as well as history, for using bcrypt to hash your passwords.
Regarding the performance aspect, at least in the Ruby library, you can determine how thorough you want it to be regarding cpu usage. I can't speak for other libraries but I'm sure there are similar options. There are lots of ways to combat a DOS attack, such as rate limiting, that could (and should) be employed.
In the event that the database and code are stolen, arguably, having one less thing to worry about is just fine with me. :) Realistically, both of these scenarios are pretty unlikely, and people are far more inclined to try sql injection first.
Having read through the article, he makes some good points. Having one less thing to worry about in a catastrophic situation is certainly valid.
There was a point in that article which I think is the single most important concept here: “The major advantage of adaptive hashing is that you get to tune it. As computers get faster, the same block of code continues to produce passwords that are hard to crack.”
Brute-forcing a hash (provided you have the salts) is a forgone conclusion if you want it badly enough, given enough computing power, whether it's an incredibly expensive bcrypt, or a fast MD5, or whatnot; the access to cheap, easy cloud computing ensures that. What I hadn't considered is the ability to adaptively tune the speed of your hash algorithm to account for increases in computing power. At the end of the day, you just want a password that costs more to brute-force than the value you'd get out of it.
Consider me won over, with the caveat that you have to be very careful to not expose unthrottled access to a mechanism that'd allow a user to invoke that hashing, since a targeted attack could be devastating at expensive-enough hash times.
I'll definitely give you that regarding a targeted attack. If you're using Rails/Rack, one possible solution for that is Rack::Throttle at http://github.com/datagraph/rack-throttle. I think at some point I'll do a round up of those as well. Thanks a lot for the comments.
Drupal uses MD5 by default but PHPass and AES are available via third-party modules.
Thanks a lot! I've added that to the list.
I hate passwords. :-P
I hate passwords. :-P
After all an interesting blog post, though! :)
Thanks.
Glad you liked it, thanks a lot.
hi nice blog^^