DISCLAIMER: Rainbow tables are complicated. For precise details, please read the Wikipedia article. For a simplified introduction with specific examples, keep reading.
To crack passwords, you can use a rainbow table. A rainbow table has two columns: password, and cryptographic hash of the password. To protect against rainbow table attacks, you can use a salt.
By "salting the password", you don't store the hash of the password in the database. The salt is a long, random string.
then store both salt and hash(password + salt) in your table.
If you store hash(password) directly, you make it easier for a malicious actor to find user passwords from your table. That malicious actor can precompute a bunch of password hashes, and look up whether there's a match. You can find tables like that on the internet, for instance as a 5 GB text file.
We're going to make a small rainbow table, and use it to "crack" a password. To avoid lots of wainting, we're going to stick to passwords that are easy to crack:
If we were to make our table harder to crack, we would make different choices:
A rainbow table can be used to look up password
from hash(password)
. We choose base64_encode(sha1(password))
as our hash function:
We use our hash function like this:
To create a lookup table, we enumerate all three letter combinations from an alphabet:
We will use a small alphabet:
Why? We just don't want to wait a lot while working on this code. I like to keep the feedback loops short when I code to learn. For real-world rainbow tables to guess Windows XP passwords like ophcrack, it could take hours to days to create a rainbow table.
We have an index of 216 passwords in our rainbow table :)
The first ten pairs of hash(password), hash are:
hash(password) | password |
---|---|
+99bVmImquPJRER4D1V3KVxrThg= | oao |
+IzQ8ziz+stNITONaYuQMi1iPpc= | aoo |
+JYqEfj47f05XlI2RlfemBmJd88= | tbc |
+S86DuZI82iApIIJnMZvxK/NPxw= | btc |
+l2u4RVgRDeDeO+P4Ag76i6VvFY= | bbt |
//tJebLry9f7P9v9fKaxowrwq30= | eet |
/404sEHjiTUwfCYc3Skj6bnl3AA= | bba |
/IRSfZHWZzm5fKJ4pqGFNgkKMOY= | eeo |
/du4zJqHbaGhZYzboXDgo1erhek= | tba |
/jwD4BurKygOk/Hd1CJWq3dnBRg= | ata |
Our function from hash to password is a map lookup!
hash(passord) | passord |
---|---|
qZk+NkcGgWq6PiVxeFDCbJzQ2J0= | abc |
nZiejSfcng7DOJ/IVfFCw9QPDFA= | cat |
Q3ocFO+qjpiB72uwd0EdwdJMtMA= | teo |
Voilà! We can now lookup certain passwords if we have the password hash.
But there are limitations:
It's easy to make mistakes when you roll your own system for securing user accounts without experience in information security. And there are plenty of pitfalls we haven't touched.
But at least you now know some examples of what can go wrong when you push ahead without considering how to secure user data!
Above, we computed the hash of all three letter passwords using the letters abceot
. When humans create their passwords, we can do better! For instance, we can start with a list of common passwords:
https://en.wikipedia.org/wiki/Wikipedia:10,000_most_common_passwords
Try it yourself! 😊
Thank you to Jack Rusher for reading an early version of this text. Any errors are mine.
You are viewing an immutable version of this text. If I fix errors, you may not get the fixes. A link to the latest version of this document can be found here: