codahale.com٭blog

This is my old blog. My current writing is here: codahale.com

bcrypt-ruby: Secure Password Hashing

The Problem

So you remember how Reddit got a backup copy of their database stolen? Do you also remember how, since they stored users’ passwords as plain text, the hacker also got a big list of people’s email addresses and their passwords?

That’s bad. How can you avoid that?

Using bcrypt-ruby

Check out my new gem: bcrypt-ruby.

To install:

sudo gem install bcrypt-ruby

(make sure you have a C compiler and OpenSSL)

To use:

require 'bcrypt'

my_password = BCrypt::Password.create("my password") #=> "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa"

my_password.version              #=> "2a"
my_password.cost                 #=> 10
my_password == "my password"     #=> true
my_password == "not my password" #=> false

my_password = BCrypt::Password.new("$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa")
my_password == "my password"     #=> true
my_password == "not my password" #=> false

What’s bcrypt?

bcrypt() is the password hashing algorithm used by OpenBSD.

It’s awesome because:

  • Developed by The OpenBSD Project specifically for hashing passwords. They don’t screw around with security.
  • Salts are automatically generated and managed for you.
  • It’s orders of magnitude harder to crack than MD5, SHA2, and other standard hash algorithms.
  • It has a cost parameter which allows you to ratchet up the computational expense of checking a password — it can be low for low security situations or high for high security situations.

So don’t get caught with your pants down — be professional.

30 Responses to “bcrypt-ruby: Secure Password Hashing”

  1. footle » Do Passwords Right With bcrypt-ruby Says:

    [...] colleague Coda Hale just released a sweet bcrypt-ruby gem that does password hashing right. It also provides for future-proofing by enabling you to assign a [...]

  2. mfp Says:

    s/magnitudes of order/orders of magnitude/

  3. Coda Says:

    Thanks, mfp. I type good.

  4. Pit Says:

    bcrypt seems to work with the Windows One-Click-Installer too. It was easy to compile it with MinGW (I just had to define the missing types u_int8_t, u_int16_t, and u_int32_t). All your tests pass. So you might consider to offer a precompiled version for us Windows users.

  5. Coda Says:

    Pit — I don’t use Windows, but I’d be happy to work with someone to coordinate Windows releases. Email me if you’d like to do that.

  6. Chris Says:

    Hey, hey, hey … this looks like some great stuff. Thanks, Coda!

  7. Lee Says:

    For the record I’m in love with OpenBSD and everything they do.

    I’m trying to understand how the automatic salt generation really benefits you here. For example, the code seems to extract the first 29 chars from the hash and use it as the salt when hashing a new password to test against. Is this just the way bcrypt works? It seems to me that knowing this makes the salt ineffective at warding off brute force attacks. If anyone with the library can generate hashes with the known salt it’s kind of useless.

    So if I understand the code correctly, if your database or a backup is compromised, a cracker can take the now known hashes and brute force the passwords. Granted bcrypt will help because of the computational difficulty of generating the hashes, but the salt doesn’t help at all. It would only prevent someone from generating a pre-defined database of hashes to compare against (trying to subvert the computational difficulty of bcrypt).

    That said I’m no cryptanalyst and could certainly be missing something. I welcome additional enlightenment.

  8. Coda Says:

    Lee– You seem a bit confused about the role of the salt in hashing passwords. Salting passwords is a technique to increase the cost of attacks which could occur after a systems breach. The question we’re looking to answer is: a hacker gets a copy of your database — what now?

    The risk is that if someone gets a copy of your database, they could also get copies of the users’ passwords. The less information an attacker gets, the better.

    Depending on how you store your passwords, the attacker has different options.

    If your passwords are stored in the clear, like Reddit, the attacker must use his or her visual cortex to decode a matrix of pixels into a series of graphemes. (In other words, they have to be able to read to recover all the passwords in the database.) This is stupid easy.

    If your stored hashes are simply hash(user_password), then the attacker must figure out what hash algorithm you’re using (either by guessing based on digest size or by looking at the code running your web site). Once they’ve done that, then they go and download a rainbow table (a huge database of password+hash pairs) for that particular algorithm. From there they look up hashes and record the matching password and recover all the passwords. This is slightly harder — the attacker needs disk space, memory, bandwidth, etc.

    If your stored hashes are something like hash(user_password + system_secret), which is I think what you’re suggesting, then the attacker must also get a copy of the system secret. Since they’re already in your server, this isn’t complicated — your application servers have to have a copy of this secret in order to authenticate users as they log in. While they’re in there, they can also figure out what algorithm you’re using. Given the algorithm and secret, they can then run a list of potential passwords (plus the secret) through the hash algorithm and make their own rainbow table. From there, they can then recover all the passwords. This is hard — the attacker needs all the resources of the previous attack, plus the time and computational power to build their own rainbow table.

    If your stored hashes use a per-password salt (hash(salt + user_password)) like bcrypt() does, then the attack must brute force each individual password separately — in effect, calculating a rainbow table for each password. This is super hard — the attacker must expend the same amount of effort as in the previous attack for each password.

    In theory, you could use a per-password salt, but then try and keep the salt secret. Keeping the salt secret doesn’t buy you much, since you’re already presumably protecting your database as much as you possibly can. If someone can get to your database, then the odds of them being able to get to your secret salt box is really high. It would make things harder on an attacker who got a copy of the database from a backup tape, but you’d also need to be backing up the secret salt box — if you lost it, you wouldn’t be able to authenticate users.

    bcrypt-ruby is a friendly object wrapper to OpenBSD’s bcrypt(), to the point where you can use it on an OpenBSD machine to authenticate users against /etc/master.passwd. So I’m not proposing a new form of password storage here, I’m letting Rubyists in on the best practice.

    If you’ve got more questions about this, I suggest getting a copy of Ferguson & Schneier’s Practical Cryptography. Hell, I suggest getting a copy even if you don’t have more questions. ;-)

  9. Lee Says:

    Thanks for the info Coda. You’re right, the real problem is never really understanding why you’d salt passwords in the first place. I hadn’t ever considered that a cracker might pre-compute or obtain a hash to password mapping for your algorithm. Seems kind of obvious now though ;-) I had wrongly assumed that hashing alone would necessitate brute forcing each password. You’re correct that only the per-password salt would force that. This scheme seems like a real great approach. Thanks for the work and clarification.

  10. Coda Says:

    No problem, Lee. Part of the reason I wrote this library is to make it so that folks can have high-quality password storage without needing to be a serious geek about it. It was pretty obvious that most folks working on web applications didn’t have the knowledge required to design their own authentication systems (and I’m not saying that as an insult — designing authentication systems is super hard, which is why I lifted mine from OpenBSD), and that users would eventually pay the price for that (see Reddit).

  11. Dan Kubb Says:

    Coda, I’m using the latest bcrypt with Rails and I noticed it has a tendency to blow up when Rails does something like password == false. To fix it I changed line 141 of lib/bcrypt.rb in the “==” method from:

    super(BCrypt::Engine.hash(secret, @salt))

    to:

    super(BCrypt::Engine.hash(secret.to_s, @salt))

    I also had one issue earlier when requiring bcrypt. It wasn’t finding etc/bcrypt_ext, so I changed the require on line 3 of lib/bcrypt.rb to:

    require File.dirname(__FILE__) + ‘/../ext/bcrypt_ext’

    There’s probably a better fix, but I figure I’d mention it in case others are having problems too.

  12. Coda Says:

    Dan — Thanks for letting me know. Both are stupid errors of mine, and both are fixed in 2.0.1, which is making its way to the gem mirrors right now.

  13. winson Says:

    Hi,

    I always get “invalid hash” while I follow steps to put bcrypt on my Rails. It seems like “user.pass ==” operation not work well.

    Is there any issues when install bcrypt on OSX or MySQL5??

  14. Coda Says:

    winson — bcrypt-ruby works fine on OS X, as long as you have a compiler installed. I could help you more with your specific problem if you emailed me some sample code which raised that exception.

  15. winson Says:

    Coda,

    I try bcrypt-ruby in my OSX irb environment, it works very well. Thanks for your help, I will mail you sample code while I’m back home.

    Winson

  16. dburkhalter Says:

    Coda,

    I also need the database to be readable by a regular application. For example, users need to get their purchased license codes from the web. The company writes a program to generate those license keys but does not want the security risk of exposing that code to the internet.

    Which bcrypt c source can be used to be compiled into an regular application that can access the database that the rails web app can access as well?

    Thanks,

    dburkhalter

  17. Coda Says:

    dburkhalter: bcrypt is a patent-free algorithm, so you can either find an existing implementation or write your own. The rest depends entirely on the legal and technical situation.

  18. links for 2007-05-02 (Hej Varlden) Says:

    [...] bcrypt-ruby: Secure Password Hashing Gem för att spara lösenord på ett säkert sätt. (tags: Ruby Plugin) [...]

  19. Philip Says:

    I had to use this to import the bcrypt library properly after installing the GEM (a require ‘bcrypt’ by itself didn’t work):


    require 'rubygems'
    gem 'bcrypt-ruby'
    require 'bcrypt'

    ...

  20. alex Says:

    huh,

    how nice, the slogan of RoR is to faast development,
    but,
    “for windows you must compile”… what? why ?
    i dont know how to do it,
    okay i’ll go to google and waste time to figure out “how-to” ?
    with such time wasting we dont need ROR,

    i am using plugins/gems - none of them tagged with “osx’able/linux’able only”.

    The bad thing - i cannot do anything now, because project are shared and i need work from windows.

    going to google….

  21. Coda Says:

    Alex — I don’t use Windows, and as such have fairly little sympathy for you.

  22. alex Says:

    i dont speak whick os you should work, everyone choose for him self.

    i am speaks about that everyone speaks about RoR is superfast development, and where is that time ?

    Also i dont saying you are poor ..
    Great plugin works well and clear, i just stopped to develop very important part of project because im working remotely with vista.

    thats all.

  23. curious Says:

    given bcrypt relies on blowfish encryption, can bcrypt hashed password be decrypted? other words, can bcrypt generated hashes be considered non-reversible, like md5 and sha generated hashes?

    thanks.

  24. Coda Says:

    curious– bcrypt is not the same algorithm as Blowfish — it’s based on eksblowfish, which is a Blowfish variant with a key schedule designed for computational expense. bcrypt uses eksblowfish to encrypt a fixed string (”OrpheanBeholderScryDoubt”, for whatever reason) with the password. The password is not encrypted, and there’s no way to recover the password from the hash without trying candidate passwords (i.e., brute force or dictionary attacks). You can read the technical paper here: http://www.usenix.org/events/usenix99/provos.html

  25. Frank Says:

    Looks like a great plugin - can anyone provide instructions on compiling for Windows? I’ve been googling for a couple of hours now without much success. Thanks!

  26. Dan42 Says:

    This is a nice gem but errors really should inherit from StandardError (or possibly SecurityError), never from Exception. As it is, any error will shut down irb and/or bypass “rescue => err” blocks. :-(

  27. Coda Says:

    Dan, that’s a really good point. That’ll be changed in the next release.

  28. Louis Says:

    Hi - Did anyone have any luck installing/compiling in a Windows environment? If so it would really be appreciated if you shared your knowledge! Thank you.

  29. thinking_critically Says:

    Yo, Coda. I often use SHA-2 with salt for password storage. The claim I read is that the hashing algorithm is superior to SHA-2. I would have to caution readers to be careful about using a little known hashing algorithm, following the advice of Bruce Schneier. He advocates using algorithms with sound mathematical basis, that are open, and subject to much public scrutiny. SHA-2 or RIPE are the best of these at the moment, and a double hashing technique with salt can give attackers a hard time.

    So, where can I find this algorithm for review purposes? What exhaustive statistical tests have been performed? Has it been studied by a large number of cryptography professionals and NSA sanctioned like SHA-2? I’d appreciate any information, as I can always use another good hashing algorithm (if it is one).

  30. Coda Says:

    thinking_critically: The technical details of bcrypt were first published at USENIX ‘99: http://www.usenix.org/events/usenix99/provos.html. You can read there how it differs from most general-purpose hash algorithms (i.e., it’s built for generating stored password hashes). It’s well-tested, open, and has been widely evaluated via the OpenBSD project.

    (This information is contained in the README and in previous comments on this page, not to mention easily available via Google.)