On Passwords
jasonseifer.com
jasonseifer.com
In addition, bcrypt() allows you to increase the amount of work required to hash a password as computers get faster. Old passwords will still work fine, but new passwords can keep up with the times.
Obviously, old passwords would be weaker than new passwords this way. An easy solution to this problem is to check the cost of hashed password in your database when a user tries to log in. If the cost is lower than the cost you want, and the password matches, then replace the hash at higher cost using the password the user just gave you.
As for hashing SSNs, you can easily overcome the problem of a limited range of data by the usual solution: salting. Bcrypt-ruby does salting automatically, which you can observe by rehashing a password in irb multiple times, getting different results (the salt is also stored with the hash, which makes this possible). You can also add additional salt yourself if you really want to.
Here's another idea: Use a secret salt, similar to AWS's secret id. Make it long enough that it would pretty much impossible to brute-force (do the calculations). Does that seem like a workable solution? Of course, if the "secret" isn't secure, then well, you're in trouble, and if the secret is in your code in plaintext... Yeah, it's an uphill battle. I'm sure someone more experienced than me here can provide some solution.
2) bcrypt does not appear to be well documented, well analyzed, or well maintained in the security community. Security through obscurity is not a good thing, as you have no idea what your exploitation window looks like.
3) If your goal is to limit the effect of rainbow tables, and not storing the password in the clear, and do not need to retrieve the original password, the ideal solution is to store a hashed password on disk and a salt for that hash. Thus your salt rotates for every password created/saved and you recovery of all passwords requires a rainbow table for every password. Not a simple task.
4) Lastly, you should make yourself familiar with the security risk of any system you use. For example, you shouldn't generally be using MD5 anymore...
You do realize that this is how all modern "cryptographically secure" hash functions work, right? For that matter, so do the symmetric-key block ciphers that hashes are closely related to. All of them simply use S-boxes to obfuscate the numbers, followed by repeated "rounds" of simple bitwise primitives (bitshift, and, xor, perhaps addition modulo a power of 2). There's no inherent mathematical reason why combinations of these simple functions should be strong, when each function individually is weak.
This is precisely why cryptography is such a tricky field to work in.
There is inherent mathematical reasons why the combinations of the functions should be strong. It's why s-boxes are accepted practice.
Agreed, cryptography is a tricky field. All the more reason to stick to systems that are carefully reviewed by experts smarter them myself and you.
...you shouldn't generally be using MD5
I thought md5 is discouraged precisely because it is so fast to compute, hence allowing brute-force attacks to succeed in relatively short times.
1) Taking longer is not an indication of strength of the
algorithm
Is anyone arguing it is?What's more, if I could do 1 billion MD5s per second, then I could brute force every possible 8 character base-64 password (for one user at a time) in merely 3.25 days. Even if that one user has chosen his or her individual password by pulling it straight from /dev/random. Impractical against a site with 1000 low-value users? Sure, most black hats won't want to spend 5 to 10 years of compute power on that. But that's an utterly practical attack against a single, high-value account. If you're Twitter and someone just stole your salted password hashes, you'd better ring up Ashton Kutcher in the next 24 hours and tell him to change his password right now.
Something truly scary: this level of computer power can be easily achieved today using parallelism. I just now ran a benchmark on my personal Linux machine against 10,000 small files on a ramdisk, each of a size appropriate for salted passwords and each with unique data. According to this benchmark, my Athlon64 running at 800MHz can MD5 100,000 passwords per second using md5sum, complete with the system call overhead of opening and closing ten thousand files. Even so, my machine is only 4 orders of magnitude slower than the monster I described, and one of those orders of magnitude should be written off because my processor is years behind the times (sub-1GHz and single core). Shave off another order of magnitude due to the useless system calls, and one hundred modern $100 commodity processors could do this job today, and you could probably do it for half the price or less if you used DSPs or video card GPUs. This is trivially within the range of a project like Seti@Home or Folding@Home, or a dark-hat version of the same (running on a botnet of stolen CPU cycles).
Addendum: also note, these figures imply that one computer with a modern CPU can dictionary attack a salted password hash file from a 1000-user site in merely 200 seconds, i.e. about the time it takes to microwave a burrito. No need to build a botnet first.
Essentially, MD5 and SHA were designed to be fast hashing algorithms. This is NOT what you want with password hashing, as you need to be able to tune the complexity of your algorithm to scale with Moore's Law and other factors.
He has also discussed this in depth here in the past if you search back over his comments (tptacek)
1. http://chargen.matasano.com/chargen/2007/9/7/enough-with-the...