The question is never “which hash is best?” It is “what am I hashing, and what attack am I slowing down?” A fast hash is a feature for file integrity and a disaster for stored passwords.
Short answer: use SHA-256 or SHA-512 for integrity when the attacker cannot replace the expected digest; use Argon2id for new password storage; use bcrypt only where compatibility makes it necessary; do not use MD5 or SHA-1 for security decisions. The OWASP password-storage cheat sheet recommends Argon2id and gives current parameter guidance.
| Job | Use | Do not use |
|---|---|---|
| Download checksum | SHA-256 plus a trusted published digest/signature | MD5 as a tamper guarantee |
| Password storage | Argon2id with a unique salt | Plain SHA-256, even salted |
| Legacy password verification | bcrypt while planning migration | A forced reset without a migration path |
| Message authentication | HMAC-SHA-256 | sha256(secret + message) |
Password hashes must be expensive
// Pseudocode: use a maintained Argon2 library, not this as a crypto implementation.
const encoded = await argon2id.hash(password, { memoryCost: 19456, timeCost: 2, parallelism: 1 });
const ok = await argon2id.verify(encoded, suppliedPassword);
The encoded result carries the algorithm, salt, and work parameters. That makes gradual upgrades possible: on a successful login, verify the old hash, then rehash with stronger parameters.
Encryption is reversible with a key. Hashing is one-way. Encrypting passwords so you can recover them later is usually a design bug, not a convenience.
The mistake behind “we use SHA-256”
SHA-256 is designed to be fast. An attacker with a stolen password database can make enormous numbers of guesses cheaply; salts stop precomputed rainbow tables but do not make guessing expensive. Argon2id and bcrypt are deliberately slow, and Argon2id also consumes memory, which raises the cost of parallel guessing.
Pick the primitive by purpose, store passwords with a maintained password-hashing library, and benchmark parameters on your own hardware. Crypto names are not interchangeable labels.
Cover photo by Laura Gigch on Pexels.
