Imagine signing up for an account on VerySecureWebsite.com with your email and password. Soon after, you get an email: the site was hacked, and usernames and passwords stored in plaintext are now on the dark web. As you rush to change passwords everywhere (hoping you don't reuse them), you wonder: Shouldn't passwords be encrypted so hackers can't read them?
You're absolutely right. Reputable web services store passwords using salted hashes—ideally slow ones with added protections like peppering. Don't worry if this sounds like a cryptography recipe; secure password storage is straightforward when done right.

| Username | Password Entered | Input to Hash | SHA-256 Hash |
|---|---|---|---|
| Unintentional user | Weak password | Weak password | 252E2406732308F9A7B378ED94E78467D14077D19C8282443FCD3D6190FCABFA |
A hash function is a one-way trapdoor: it transforms your plaintext password into a fixed-length string of characters, but you can't reverse it to recover the original. No key needed—just verification.
Secure sites handle logins like this:
Hackers get only gibberish hashes from a breach. Strong algorithms like SHA-256 make reversal computationally infeasible.
But hashes aren't invincible. Attackers use dictionary attacks: hash common passwords and match against stolen hashes. Precomputed rainbow tables speed this up for popular passwords—another reason to avoid "password123".

| Username | Password Entered | Salt + Password | SHA-256 Hash (salt=XcyKn42 prefixed) |
|---|---|---|---|
| Accidental user | Weak password | XcyKn42Weak password | 13EB660FF7FBD29A728FC592297D78DF19AFF879736315FBF1F1C4B7123BD10C |
Salt is a unique random string added before hashing each password. Even tiny changes alter the entire hash, ruining precomputed tables. Salts are stored plainly (per user), forcing attackers to recompute hashes for each one—a massive slowdown.
Still crackable with GPU-powered guesses (billions per second), but far slower than unsalted attacks.

| Username | Password Entered | Input to Hash | Bcrypt Hash (salt=XcyKn42, 12 rounds) |
|---|---|---|---|
| Unwitting User | Weak Password | XcyKn42Weak Password | $2y$12$6OleutQBO2iPoNvgpyDndOU26Lqt9Y34f6PLEOxmCELP5GoswrJT. |
Algorithms like bcrypt or PBKDF2 use key stretching: thousands of iterations to make hashing deliberately slow (e.g., 100ms per attempt). Legit logins are fine; brute-forcing billions becomes impractical.
| Username | Password Entered | Input to Hash | Bcrypt Hash (salt=XcyKn42, 12 rounds, pepper=|4|/|@p3pp3r appended) |
|---|---|---|---|
| Unwitting user | Weak password | XcyKn42Weak password|4|/|@p3pp3r | $2y$12$njmWr5UMydCzdCE44ElW/OIfYp2PH9sgonCATyVY.OVKSpmoSaZlu |
Pepper adds a secret server-side value (one per site, not stored with hashes). Breaches expose only salted hashes; without pepper, cracking restarts.
Don't re-hash hashes—it compounds weaknesses. Use proven libraries (bcrypt, Argon2). Assume breaches happen: craft unique, long, complex passwords (or use a manager). As cybersecurity experts recommend, layer defenses for robust protection.
Image credits: Hash function illustrations