Rainbow Table Attacks
Rainbow tables are precomputed lookup tables that trade storage space for cracking speed, allowing attackers to instantly reverse unsalted hashes. You will learn how rainbow tables work, why they were once the dominant password cracking technique, and how password salting completely defeats them. Understanding rainbow tables explains why salting is a non-negotiable requirement in modern password storage.
Learning Objectives — Deep Dive
Each objective below is broken down into plain English so you know exactly what you are learning, why it matters, and what you will be able to do when you are done. Mark each one complete as you work through the lesson.
Generate an unsalted MD5 hash
Run 'echo -n "password" | md5sum' to generate the MD5 hash of the word 'password' — you should get '5f4dcc3b5aa765d61d8327deb882cf99'. This exact hash appears in every rainbow table and online hash lookup database because it is one of the most commonly used passwords. Run 'echo -n "123456" | md5sum' and 'echo -n "qwerty" | md5sum' and note those hashes as well. The key insight is that without a salt, every user who chooses the same password will have the identical hash — an attacker who cracks one hash has cracked all accounts using that password simultaneously.
Look it up online
Navigate to crackstation.net and paste the MD5 hash of 'password' (5f4dcc3b5aa765d61d8327deb882cf99) into the search field. CrackStation will instantly return the plaintext 'password' because it maintains a massive precomputed lookup table of common password hashes. Try the hashes for '123456', 'qwerty', and 'letmein' — all will be found instantly. This demonstrates why unsalted hashes of common passwords provide essentially zero security — the lookup is faster than the time it takes to type the hash. Also try hashes.com and md5decrypt.net to see multiple lookup services returning the same results.
Generate a salted hash
Run 'python3 -c "import hashlib, os; salt = os.urandom(16).hex(); h = hashlib.sha256((salt + 'password').encode()).hexdigest(); print(f'Salt: {salt}\nHash: {h}')"' to generate a salted SHA-256 hash. Run the command three times and observe that each run produces a completely different hash even though the password is the same — the random salt makes each hash unique. Paste the salted hash into CrackStation and confirm it returns 'Not found' — the precomputed lookup tables are useless against salted hashes because the salt must be known to compute the correct hash. The salt does not need to be secret; it is typically stored alongside the hash in the database.
Understand the defense
The correct defense against password cracking is to use a purpose-built password hashing function: bcrypt, scrypt, or Argon2 (the winner of the Password Hashing Competition). These functions are intentionally slow and memory-intensive, automatically handle salting, and include a configurable cost factor that can be increased as hardware gets faster. Never use MD5, SHA-1, or SHA-256 directly for password storage — they are designed to be fast, which is exactly the wrong property for a password hash. In code, use your language's built-in library: Python's 'passlib', Node.js's 'bcryptjs', PHP's 'password_hash()', or Java's Spring Security's 'BCryptPasswordEncoder'.