I really appreciate that in programming we can get a lot done without having to think about everything at once. We put enormous amounts of trust into our tools, libraries, and frameworks. I used to feel guilty for not knowing everything about a library or topic that I used everyday. Over the years I’ve learned to exchange that guilt for the enjoyment of digging in to understand something better when the opportunity arises. I can’t go down a rabbit trail every time I see one, but when I have to time to spare I really try to approach it with excitement rather than begrudgingly having to learn yet another thing.
A while back I was working on a Rails app using the Devise authentication gem. It struck me that I didn’t know where the salts were.
The next few paragraphs are a gross oversimplification, but if you aren’t familiar with the term salt, it’s a string added to a password before it’s hashed. When creating a cryptographic hash from a password string, you’ll want to be able to generate the same hash again later. Generally speaking, this is how encyrption algorithms behave; given then same input you’ll get the same output:
> Digest::SHA256.hexdigest 'abc' => "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" > Digest::SHA256.hexdigest 'abc' => "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
However, it’s important to add some characters to the password, otherwise a huge list of precomputed hashes (usually called a 🌈 rainbow table) could used to figure out the password based on the hash. Adding even a few extra characters generates an entirely different hash.
> Digest::SHA256.hexdigest 'abc' + 'asdf1234' => "4da31782c98a4aa9647b077a2fc5f892ca44c41357010bc5a5994f7e1049155c" > Digest::SHA256.hexdigest 'abc' + 'zxcv6789' => "21b3651a2be227f41360468ad2b2643e0f97361f4caf40e44ac9359282d121dd"
When a user logs in, you would add the same salt that you used when you stored their encrypted password. If the user’s entered password plus the salt you add generate the same encrypted hash, you know this is the same password that initially entered when setting the password. This means you need to store the salt(s), as you’ll need them each time the user authenticates.
I’ve seen salts stored in all sorts of places, and in the Devise gem’s case I realized I’d never thought about where the salts were. After looking around for a few minutes I figured I was missing something, and it turns out I wasn’t the only one wondering where they were. I found How Can Bcrypt Have Built-in Salts? on Stack Overflow to be a great question and answer, as well as lots of helpful extended reading.
The short answer to my question is that the salt is built into the string stored in the
encrypted_password column. In the example below, the
$s are delineators and the third chunk’s first 22 characters are the salt.
By using a unique salt per password, the difficulty of building a rainbow table is greatly increased. If your database was compromised, the attacker would have to generate very large rainbow tables per password.
If you’re interested, bcrypt is actually doing much more than this. The author of bcrypt’s Ruby bindings has a great post with further reading, the Ruby binding’s GitHub README has additional details, and you can check out the original research paper from the OpenBSD project if you need even more detail 😺