Storing user passwords is one of the most security-critical operations in any application. The rules are clear and non-negotiable: never store passwords in plaintext, and never store them with reversible encryption. Passwords must be stored as hashes — but even that requires doing it correctly.
Why Simple Hashing Isn't Enough: Rainbow Tables
If you simply store SHA-256(password), you're still vulnerable. Attackers maintain rainbow tables — massive pre-computed databases mapping common passwords to their SHA-256 hashes. If your database is breached and an attacker sees the hash ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f, they can instantly look it up and find it corresponds to "Password1". No computation required.
Defense Layer 1: Salt (Unique Per User, Stored in Database)
A salt is a randomly generated string that is unique to each user. Before hashing, the salt is appended (or prepended) to the password. The salt and the resulting hash are both stored in the database.
# Without salt — two users with the same password get the same hash
Hash("password123") → "e7cf3ef..." # Alice's stored hash
Hash("password123") → "e7cf3ef..." # Bob's stored hash (identical — attacker knows both use "password123")
# With salt — same password, different hashes
salt_alice = "xK9mP2qR" # randomly generated, stored in DB
Hash("password123" + "xK9mP2qR") → "a3f91bc..." # Alice's stored hash
salt_bob = "tL4nW7vZ" # different random salt for Bob
Hash("password123" + "tL4nW7vZ") → "d82e4fa..." # Bob's stored hash (completely different)
Salting defeats rainbow tables because an attacker would need to build a separate rainbow table for every unique salt — computationally infeasible at scale.
Defense Layer 2: Pepper (Shared Secret, Stored Outside Database)
A pepper is a secret value — similar to a salt — but it is not stored in the database. Instead, it lives in the application's environment (e.g., an environment variable, a secrets manager like HashiCorp Vault, or a Hardware Security Module).
# Full password hashing with salt and pepper
stored_hash = Hash(password + salt + pepper)
The database stores: username, salt, stored_hash.
The application server holds: pepper (secret).
If an attacker breaches the database, they obtain the hashes and salts but not the pepper. Without the pepper, they cannot reproduce the hash function's inputs, making brute-force attacks impossible even with the full database dump.
Defense Layer 3: Use a Password-Specific Hashing Algorithm
General-purpose hash functions like SHA-256 are designed to be fast — they can compute billions of hashes per second on modern hardware. This is great for file integrity checks but terrible for password storage, because it makes brute-force attacks fast too.
Password hashing algorithms are intentionally slow and memory-intensive:
- bcrypt: Widely used, has a configurable "cost factor" that controls how slow it is. A cost factor of 12 means 2^12 iterations. Increase the cost factor as hardware gets faster.
- Argon2: The winner of the 2015 Password Hashing Competition. The current recommended standard. Configurable for time cost, memory cost, and parallelism. Argon2id is the recommended variant.
- scrypt: Memory-hard algorithm, resistant to GPU-based attacks.
# Example using Python's passlib library with Argon2
from passlib.hash import argon2
# Hashing a password (salt is generated automatically)
hashed = argon2.hash("user_password_here")
# hashed = "$argon2id$v=19$m=65536,t=2,p=1$..."
# Verifying a password
is_valid = argon2.verify("user_password_here", hashed) # True
A common interview question is: "How do you store passwords securely?" A weak answer is "hash them with SHA-256." A strong answer covers all three layers: (1) use a slow, password-specific algorithm like Argon2id rather than a general-purpose hash, (2) the algorithm automatically handles per-user salting, and (3) optionally add a pepper stored outside the database for defense-in-depth. Bonus points for mentioning that the cost factor should be tuned so hashing takes ~100-300ms on your hardware, and re-tuned as hardware improves.