Why general-purpose hashes are the wrong tool
SHA-256, MD5, and similar algorithms are engineered to be fast — hashing gigabytes of data quickly is exactly what they're designed for. That's the wrong property entirely for password storage: speed is precisely what lets an attacker who steals a password database try billions of guesses per second on commodity GPUs. Password hashing needs the opposite property — deliberately, tunably slow — so that even with stolen hashes, brute-forcing becomes computationally expensive at scale, not just theoretically possible.
What salting actually solves
A salt is random data unique to each password, stored alongside the hash and combined with the password before hashing. Without a salt, two users with the identical password produce identical hashes — an attacker with a precomputed table of common password hashes (a "rainbow table") can crack every match instantly. With a per-user salt, even identical passwords produce completely different hashes, making precomputed tables useless — the attacker must brute-force each hash individually, salt included.
hash("password123") → same output for every user with this password
hash("password123" + unique_salt) → different output for every user, even with the same password
Bcrypt, scrypt, and Argon2 — what actually differs
| Algorithm | Year | Key property | Tunable cost |
|---|---|---|---|
| bcrypt | 1999 | CPU-hard, well-tested, widely supported | Work factor (rounds) |
| scrypt | 2009 | Memory-hard — expensive to parallelize on GPUs/ASICs | CPU + memory cost |
| Argon2 | 2015 (Password Hashing Competition winner) | Memory-hard, tunable across CPU/memory/parallelism independently | All three dimensions |
The progression matters: bcrypt is CPU-hard, but GPUs and custom hardware (ASICs) can still parallelize CPU-bound work efficiently, somewhat blunting its protection at scale. scrypt and Argon2 are memory-hard — deliberately requiring significant RAM per hash attempt, which is much harder and more expensive to parallelize across thousands of GPU cores, since memory bandwidth doesn't scale the same way raw compute does. Argon2 (specifically Argon2id, the hybrid variant) is the current recommended default for new systems.
Iterations/cost factor: why it needs to be tuned, not fixed
Each of these algorithms has a configurable "cost" — bcrypt's rounds, or Argon2's iteration/memory/parallelism parameters — deliberately increasing computation time per hash attempt. This value should be tuned to the current hardware it runs on, aiming for a hashing time that's imperceptible to a real login (well under a second) but meaningfully expensive at the billions-of-attempts scale an attacker would need. This means the "right" cost factor increases over time as hardware gets faster — a value chosen in 2015 may be too weak in 2026 and should be periodically re-evaluated.
Common mistakes
- Using SHA-256, MD5, or any fast general-purpose hash for passwords, even with a salt — speed alone makes brute-forcing practical at scale regardless of salting.
- Reusing the same salt across all users, which defeats the entire purpose — salts must be unique per password, not a single shared value.
- Setting a cost factor once and never revisiting it. Hardware gets faster; a cost factor considered strong years ago may now be crackable in a fraction of the original time.
- Rolling your own password hashing scheme. Use a well-vetted, standard library implementation of bcrypt, scrypt, or Argon2 — this is not a place to improvise.
FAQ
Why can't I just use SHA-256 with a salt for passwords?
Because SHA-256 is fast — even salted, an attacker with stolen hashes can still brute-force at high speed using modern hardware; password hashing needs deliberate, tunable slowness that SHA-256 doesn't provide.
What's the difference between bcrypt and Argon2?
Bcrypt is CPU-hard but more parallelizable on modern GPU/ASIC hardware; Argon2 is memory-hard, making large-scale parallel attacks significantly more expensive — Argon2id is the current recommended default for new systems.
Do I need a different salt for every user?
Yes — a shared salt across users defeats its purpose; each password needs its own unique, randomly generated salt stored alongside its hash.
Understand hashing fundamentals with the Hash Generator — for real password storage, always use a dedicated library implementing bcrypt, scrypt, or Argon2, not a general-purpose hash.