Hashing passwords with bcrypt in ColdFusion

Sunday, February 20, 2011

Why bcrypt?

I’ve been thinking more about security lately; specifically, password safety. For a fine explanation of why you want to hash passwords instead of storing them plaintext, read Billy’s post from a while back. He’s got a good list of references as well, including NIST’s approved hashing algorithms. In addition, check out OWASP’s thorough treatment.

Now, then…

This weekend I re-read Coda Hale’s admonition about why MD5, SHA512, etc aren’t the right choice for a hashing algorithm. His point: use bcrypt, which “uses a variant of the Blowfish encryption algorithm’s keying schedule, and introduces a work factor, which allows you to determine how expensive the hash function will be” (Coda Hale’s words).

As of this writing, bcrypt does not ship with ColdFusion. Fortunately, it’s quite easy to use within CF. One thing that quite appeals to me about bcrypt is that the salt is essentially stored with the hash, and so you don’t need to store/use/remember the salt used to hash a password. Think about that.

What does it look like in ColdFusion?

In code, it means you can hash and check a password as easily as this:

//hash it
pw = "happy1.!gIlm0re";
hashed = bcrypt.hashpw(pw, bcrypt.gensalt());
//check it
match = bcrypt.checkpw(pw, hashed);

Compare that with any password hashing code you've written before. I bet it's simpler and at the same time more secure.

What’s the catch? Time… in my tests, with the default “work load”, it takes about 200-300 ms to hash and check a single password. This is also, not ironically, why it’s such an effective technique for hashing passwords, as Coda Hale describes.

How to set it up

Here’s how to do it in ColdFusion:

  1. Go to the jbCrypt homepage. Here you’ll see usage instructions as well as download links.
  2. Download jBcrypt, written by Damien Miller
  3. unzip and drop the folder into your project
  4. compile the .java into a .class file using the JDK
    1. OR download the class file, if you’re not familiar with compiling Java
  5. either put that class file into your CF’s classes directory and restart, or you can use javaloader. The code below uses javaloader

Using javaloader, you’ll do the following. I’m leaving my timers in here so you can easily compare the difference between the default workload and a higher workload of, say, 12.

<cfscript>
pw = "happy1.!gIlm0re";

jbClass = expandPath("jBCrypt-0.3");
javaloader = createObject('component','javaloader.javaloader');
javaloader.init([jbClass]);

bcrypt = javaloader.create("BCrypt");
startts = getTickCount();
hashed = bcrypt.hashpw(pw, bcrypt.gensalt());
writeoutput("created pw " & hashed & " in " & getTickCount()  - startts & " ms <br>");

startts = getTickCount();
match = bcrypt.checkpw(pw, hashed);
writeoutput("checked pw match (#match#) in " & getTickCount()  - startts & " ms <br>");


startts = getTickCount();
hashed = bcrypt.hashpw(pw, bcrypt.gensalt(12));
writeoutput("created pw " & hashed & " in " & getTickCount()  - startts & " ms <br>");

startts = getTickCount();
match = bcrypt.checkpw(pw, hashed);
writeoutput("checked pw match (#match#) in " & getTickCount()  - startts & " ms <br>");
</cfscript>

Using the code below, here’s sample output:

jbcrypt_times

Conclusion

The ability to hash and check passwords without having to manage salts, combined with the high degree of difficulty it would require to actually crack a bcrypted password, makes this an appealing solution for me. The fact that it’s so easy to use in ColdFusion is gravy.

13 comments:

Henry Ho said...

Thanks, but I don't get it. Why doesn't this method need to store the salt? Can you please explain?

Zoramite said...

It's not that it doesn't need to store the salt, but that the salt is part of the value:

"the salt is essentially stored with the hash, and so you don’t need to store/use/remember the salt used to hash a password"

So you don't have to keep track of the salt separately, it is just part of the hashed string that you store. I have seen this done in wordpress where they used a delimiter to separate the salt and the hashed value. Not sure if the bcrypt does the same here or use a different method.

Marc Esher said...

Henry, the salt is stored as part of the password. Still, that leads me to wonder why that's a good idea. If salts are supposed to be hard to guess, then how is it that storing the salt as part of the password leads to stronger protection?

I believe the answer is that with bcrypt, knowing the salt is nearly irrelevant, as the protection comes from the fact that the act of hashing takes SO much longer.

Thus, the attacker's question: "is this the right password", takes so much time to answer because each single guess is so expensive.

Zoramite said...

Marc, I don't think that it really matters if you know what the salt is, as long as the salt changes for each password.

As I understand it, the salt is there to prevent a reverse dictionary lookup. So even if they know the salt, they still have to generate an entire dictionary to find the single password. If they got an entire db of passwords they would have to do a dictionary for each one individually.

The added benefit for the bCrypt is that it is so intensive that is they did get your passwords it would take a lot longer to try and build a dictionary since each hash takes a lot longer to calculate, slowing down the dictionary building.

8riaN said...

I've been trying to understand this all day. What I've gathered is that Zoramite is right, the value you store has the salt in it, which is fine since salts are basically public. And Zoramite is right again in saying that it's the time it takes to compute that makes bcrypt secure.

The argument goes: Normal channels are too slow for dictionary, rainbow, brute force, or any other attack, so the attacker must have the hash for offline cracking - like by stealing your users table; And if they've got your hash, they have the salt, too, so the game is about making it prohibitively time-consuming to try enough possibilities to find passwords to match the hashes/salts they stole.

The strength of bcrypt is not just that it's generically slow, but that it's speed is tunable. It encorporates a "work factor" which raises the number of times the password and the salt are folded through the blowfish key algorithm by powers of two, and which gets returned along with the hash and the salt in one big result string. You can pass the work factor as a parameter of the salt generator, but it has a default value. The really tricky part is that as processors get faster, the default, or your fine-tuned factor can get higher, so you can keep the length of time to generate the hash for a new password more or less constant, without affecting old passwords, in apparent defiance of Moore's Law.

Sequenzia said...

I've been trying to get this setup. It works once but when I reload the page it stop working and just times out the page.

It just seems to randomly work. Any ideas what could be going wrong here? I am not able to get any errors, it just times out.

Thanks!

Marc Esher said...

do you see anything in your CF Logs? Have you isolated the hang to the calls to bcrypt?

8riaN said...

Add a bunch of writeLog() calls so you can tell which line is timing out, then post us the code up to that point.

ortho said...

Thanks for posting this. I ran it on CF9 on a local xp box and it worked fine.

When I ran it on a shared host, I had a problem when not using the default gensalt(). When I used say gensalt(10), I got errors. The author of Javaloader suggest I "type" the integer. I did and the problem disappeared. So use:
hashed = bcrypt.hashpw(pw, bcrypt.gensalt(JavaCast("int",20)));

Marc Esher said...

@ortho, Good to know. Thanks for posting the solution

Unknown said...

So, I just want to make sure I've got the whole process nailed down.

When creating a user, you would use:
hashed = bcrypt.hashpw(pw, bcrypt.gensalt(12));
you would store this hashed password with the username in your users table in your database.

When the user logs in, you would use:
match = bcrypt.checkpw(pw, hashed);
to test if the password they enter at login matched the hashed password in the database.

Am I on the write track or totally out to lunch??

Marc Esher said...

Yup... that's all there is to it.

PartsBin said...

Hello Marc.

Since password security is ever evolving and since this post is almost 2 years old I was wondering how you would handle password security in CF today (late 2013).

Would you change anything?