Understanding Rainbow Tables
A rainbow table is a massive precomputed lookup table used in password cracking attacks. Instead of attempting to crack passwords through brute force (trying combinations of letters, numbers, and symbols until one works), attackers use rainbow tables to instantly look up the hash value and retrieve the corresponding password. Rainbow tables represent one of the most efficient password cracking techniques available—but cryptographic salts provide a robust defense.
Understanding rainbow tables and salts is essential for anyone involved in cybersecurity, system administration, or software development. Properly salting passwords is fundamental to modern password security and a critical component of protecting against the most efficient password attacks.
How Hashing Works
Before understanding rainbow tables, it's essential to understand hashing—the cryptographic function that stores passwords securely.
The Hashing Process
When users register or change passwords, systems don't store the actual password. Instead, they:
- Take the password: User enters "MyPassword123"
- Apply hash function: System runs password through SHA-256 or similar hashing algorithm
- Store the hash: System stores the resulting hash (e.g., "5a7f6b8d9c2e1f4a7b8c9d0e1f2a3b4c5d6e7f8")
- Discard password: Original password is never stored
Benefits of hashing:
- If database is stolen, attackers get hashes, not passwords
- Hash is one-directional (can't reverse to get original password)
- Even tiny password changes produce completely different hashes
Authentication Process
When users log in:
- User enters password
- System hashes the entered password using the same algorithm
- System compares the generated hash to the stored hash
- If they match, authentication succeeds
This approach means the system never actually "knows" the password—it only verifies that what the user entered produces the same hash.
Rainbow Tables: How They Work
The Attack Concept
While hashing is secure for protecting stored passwords, it has a vulnerability: the same password always produces the same hash. This means attackers can precompute hashes.
Traditional brute force attack:
- Stolen hash value: "5a7f6b8d9c2e1f4a7b8c9d0e1f2a3b4c5d6e7f8"
- Try password: "password1" → Hash: "3a9d7b2c..." → Doesn't match
- Try password: "password2" → Hash: "8c2e1f4a..." → Doesn't match
- Repeat thousands of times until a match is found
- Time-consuming process, especially for strong passwords
Rainbow table attack:
- Stolen hash value: "5a7f6b8d9c2e1f4a7b8c9d0e1f2a3b4c5d6e7f8"
- Look up hash in precomputed table
- Instantly get: "MyPassword123"
- Attack succeeds in milliseconds
Building Rainbow Tables
Creating a rainbow table involves:
-
Generate candidate passwords: All possible passwords up to certain length
- All lowercase letters: "a" through "zzz..."
- With numbers: "0" through "999..."
- With symbols: "!#$%..." combinations
- Dictionary words and variations
-
Compute hashes: Run each candidate through hashing function
- Hash("password1") = "a1b2c3d4e5f6..."
- Hash("password2") = "f6e5d4c3b2a1..."
- Billions of hashes computed and stored
-
Store in table: Create massive lookup table
- Key: Hash value
- Value: Original password
- Organized for fast lookup
-
Use for attacks: When stolen hashes obtained, look up instantly
- Find hash "a1b2c3d4..." in table
- Instantly retrieve "password1"
Rainbow Table Scale
The computational effort is extraordinary:
MD5 Hashes:
- Table size: ~160 GB for all 6-8 character passwords
- Coverage: ~99.9% success rate on 6-character passwords
- Lookup time: ~1-2 seconds per password
SHA-1 Hashes:
- More computational effort than MD5
- Table size: ~1 TB+ for comprehensive coverage
- Still effective for shorter passwords
Available resources:
- Free: Online rainbow table services (RainbowCrack, tables available for download)
- Commercial: Precomputed tables for various algorithms and password lengths
- Custom: Organizations build custom tables for their specific targets
Real-World Attack Scenario
Scenario: Social media breach
- Hackers break into social media company database
- Steal password hashes (10 million users)
- Download publicly available rainbow table (MD5 hashes)
- Feed stolen hashes through table lookup
- Within hours, recover passwords for millions of users
- Use compromised credentials to access email, banking, other services
This happened in real breaches—LinkedIn (2012), Adobe (2013), Yahoo (2014)—where weak hashing allowed rainbow table attacks on stolen password databases.
Why Rainbow Tables Are So Effective
Speed: Looking up a hash in a precomputed table takes microseconds, compared to seconds or minutes for brute force.
Offline attack: Attackers don't need to attack live systems. They can use rainbow tables on a stolen database offline.
Reusability: The same table works against any password that was originally used to create the hashes.
Universal: If multiple users have the same password, they'll have the same hash. One lookup reveals all matching accounts.
Cryptographic Salts: The Defense
A salt is random data added to passwords before hashing. This simple but brilliant technique completely defeats rainbow tables.
How Salts Work
Without salt:
Password: "MyPassword123"
Hash function: SHA-256
Result: "5a7f6b8d9c2e1f4a7b8c9d0e1f2a3b4c5d6e7f8"
Same password always produces same hash
With salt:
Password: "MyPassword123"
Salt: "j7k3m9p2" (random)
Hash function: SHA-256("MyPassword123j7k3m9p2")
Result: "9c2e1f4a7b8c5d6e7f8a9b0c1d2e3f4a5b6c7d8e"
Different salt produces different hash
Key Properties of Salts
Randomness: Each password gets a unique, random salt
- User 1: salt = "a1b2c3d4"
- User 2: salt = "x9y8z7w6" (different, even same password)
- Same password with different salts = different hashes
Storage: Salt is stored with the hash
- Stored format:
salt:hash - Example:
j7k3m9p2:5a7f6b8d9c2e1f4a7b8c9d0e1f2a3b4c5d6e7f8 - Salt doesn't need to be secret (it's stored alongside hash)
Length: Longer salts are stronger
- Minimum: 8-12 bytes (64-96 bits)
- Recommended: 16+ bytes (128+ bits)
- Longer salts make rainbow tables exponentially larger
Uniqueness: Each password should have its own salt
- Prevents two users with same password having same hash
- Prevents recognizing when multiple users have same password
Why Salts Defeat Rainbow Tables
The multiplication problem:
Without salt:
- One rainbow table covers all users
- Hash "5a7f6b8d..." could be "password123"
- All instances of "password123" produce same hash
- One table lookup breaks all matching passwords
With salt (8-byte salt):
- For each possible hash value, there are 2^64 possible salts
- Would need 2^64 times more storage
- Instead of 160 GB table, need exabytes (not feasible)
- Cannot precompute all possibilities
Practical effect:
- Rainbow tables become useless
- Attackers must use brute force instead (slow)
- Even brute force requires trying salt combinations
- A strong salt makes password cracking impractical
Implementation Best Practices
Proper Salting
Generate unique random salt:
import os
import hashlib
# Generate random salt (16 bytes = 128 bits)
salt = os.urandom(16)
# Hash password with salt
password = "MyPassword123"
salted_hash = hashlib.sha256(password.encode() + salt).digest()
# Store both salt and hash
stored = salt + salted_hash
Verify password:
# Extract salt from storage
stored_data = retrieve_from_database()
salt = stored_data[:16] # First 16 bytes are salt
stored_hash = stored_data[16:] # Rest is hash
# Hash entered password with extracted salt
entered_password = "MyPassword123"
entered_hash = hashlib.sha256(entered_password.encode() + salt).digest()
# Compare hashes
if entered_hash == stored_hash:
print("Password correct!")
Recommended Hashing Algorithms
Not recommended (no salt support):
- MD5: Too fast, allows rainbow tables
- SHA-1: Too fast, weak collision resistance
- SHA-256: Fast (bad for passwords), no salt support in standard form
Recommended:
-
bcrypt: Includes salt generation, built-in iteration
import bcrypt hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) # Automatically handles salt -
Argon2: Modern, memory-hard, resistant to GPU attacks
from argon2 import PasswordHasher ph = PasswordHasher() hashed = ph.hash(password) -
PBKDF2: Industry standard, simple, configurable
from passlib.hash import pbkdf2_sha256 hashed = pbkdf2_sha256.encrypt(password)
Key difference: These algorithms:
- Automatically generate and handle salts
- Use key stretching (iterations) to slow hashing
- Resist GPU and ASIC acceleration
- Are specifically designed for password hashing
Salt Length and Strength
Salt size guidelines:
- Minimum: 8 bytes (64 bits)
- Recommended: 16 bytes (128 bits)
- Modern best practice: 16+ bytes
Why longer is better:
- 8-byte salt: 2^64 = 18 billion combinations (feasible for attackers with resources)
- 16-byte salt: 2^128 = 340 undecillion combinations (infeasible to precompute)
Randomness requirements:
- Salt must be cryptographically random, not predictable
- Use secure random generators:
- Python:
os.urandom() - Java:
SecureRandom - C#:
RNGCryptoServiceProvider - JavaScript:
crypto.getRandomValues()
- Python:
Historical Salt Mistakes
The Problem with Weak Hashing
LinkedIn Breach (2012):
- Used unsalted SHA-1 hashing
- 6.5 million password hashes stolen
- Rainbow tables used to recover 90% of passwords within hours
- Lesson: Salts are mandatory
Adobe Breach (2013):
- Used unsalted MD5 hashing
- 150 million password hashes compromised
- Rainbow tables recovered millions of passwords
- Lesson: Even adding salts requires strong algorithms
Yahoo Breach (2014):
- Used weak hashing algorithms
- 3 billion account hashes compromised
- Many passwords recovered through rainbow tables
- Lesson: Weak algorithms defeat even salt protection
Modern Approach
Today's best practices:
- Use modern hashing algorithm (Argon2 preferred)
- Include random salt for each password
- Use key stretching (iterations) to slow hashing
- Use memory-hard algorithms resistant to GPU/ASIC attacks
- Regularly audit password storage practices
Testing Your Defenses
Organizations should:
-
Audit password hashing: Review what algorithm is used
- Check: "What hashing algorithm are we using for passwords?"
- Good: Bcrypt, Argon2, PBKDF2
- Bad: MD5, SHA-1, unsalted algorithms
-
Verify salt implementation: Confirm salts are used properly
- Check: "Does each password have a unique salt?"
- Verify: "Are salts at least 16 bytes?"
- Confirm: "Are salts cryptographically random?"
-
Test password validation: Verify same password produces different hashes
- Create two accounts with same password
- Extract hashes from database
- Confirm hashes are different (indicates unique salts)
-
Check iteration counts: Verify key stretching is configured
- Bcrypt: Default factor 12 (2^12 = 4096 iterations)
- PBKDF2: Minimum 100,000 iterations (preferably 600,000+)
- Argon2: Verify memory cost and time cost configured
Conclusion
Rainbow tables are a powerful password cracking technique that would render passwords insecure if salts weren't used. Salts solve this problem by adding randomness to each password before hashing, making precomputed attacks infeasible. Combined with strong hashing algorithms like bcrypt and Argon2, proper salting ensures that even if attackers steal password hashes, they cannot efficiently recover the original passwords.
For developers, system administrators, and security professionals, implementing proper password salting is non-negotiable. It's one of the most effective and well-understood defenses in cryptography, and the cost of implementation is minimal compared to the security benefit. Organizations that fail to properly salt passwords leave themselves vulnerable to rainbow table attacks—a lesson learned through countless high-profile breaches.


