Hashing Algorithms Explained
"Just hash the password with SHA-256" is one of the most common mistakes in application security — not because SHA-256 is weak, but because it's fast, and fast is the wrong property for a password hash. Here's the distinction that actually matters, and what's currently recommended instead.
// the split that matters more than the algorithm name
// fast hashes: file integrity, checksums, content addressing
// password hashing needs a KDF, not a hash
A Key Derivation Function adds two things a plain hash doesn't have: deliberate slowness (a tunable work factor) and, for the modern ones, deliberate memory cost — which makes cracking expensive even on specialized hardware (GPUs, ASICs) that laugh at plain SHA-256. Current OWASP Password Storage Cheat Sheet guidance, in order of preference:
// salting — why the same password shouldn't produce the same hash
A salt is random data mixed into the input before hashing, unique per record and stored alongside the hash (it isn't secret). Without one, two users with the password hunter2 get identical hash values, and an attacker can precompute a rainbow table — a lookup of hash → plaintext for common passwords — once, then crack every unsalted database instantly on lookup. Every modern password KDF (bcrypt, scrypt, Argon2) generates and manages a salt automatically as part of its output format; you don't add one manually.
// HMAC — a different tool, often confused with plain hashing
HMAC (Hash-based Message Authentication Code) combines a hash function with a secret key to prove both integrity and authenticity — that a message wasn't altered and that it came from someone who holds the key. A plain SHA-256 hash proves integrity only: anyone can compute a SHA-256 of anything, so a bare hash alongside a message proves nothing about who sent it. This is why webhook signatures, JWT's HS256 algorithm, and API request signing use HMAC-SHA256, not raw SHA-256 — the key is what turns "a hash" into "a hash only the real sender could have produced." See What Is a JWT? for how this same HMAC construction is what actually signs an HS256 token.
// what "broken" actually means — real collision attacks
// checksums vs cryptographic hashes — not the same category
CRC32 and similar checksums are designed to catch accidental corruption (a flipped bit from a bad network or disk) cheaply — they are not designed to resist someone deliberately engineering a collision, and take microseconds even on huge inputs. Cryptographic hashes (the SHA family) are slower but resist deliberate tampering. Using CRC32 where you need tamper-resistance is a common quiet mistake — it looks the same as a real hash in a hex string, but offers none of the security guarantee.
// frequently asked questions
Why is SHA-256 wrong for hashing passwords?
Not because it's weak — because it's fast. SHA-256 is built to process gigabytes per second, which is exactly wrong for passwords: an attacker with a stolen hash database can also compute billions of guesses per second. Password KDFs (bcrypt, scrypt, Argon2) are deliberately, tunably slow, which is the entire feature that protects a stolen hash database from being cracked quickly.
What's the current OWASP-recommended password hashing algorithm?
Argon2id is preferred (minimum 19 MiB memory, iteration count 2, parallelism 1). If unavailable, scrypt (CPU/memory cost 2¹⁷). bcrypt for legacy systems (work factor 10+, note the hard 72-byte input limit). PBKDF2 only when FIPS-140 compliance is required. These numbers are a moving floor, not a ceiling — use a library's adaptive defaults rather than a fixed setting picked once.
Why does salting matter for password hashes?
Without a salt, two users with the same password get identical hash values, letting an attacker precompute a rainbow table (a hash-to-plaintext lookup) once and crack every unsalted database instantly. A salt is random data mixed in before hashing, unique per record, and every modern password KDF generates and manages one automatically — you don't add it by hand.
What's the difference between HMAC and a plain hash?
A plain SHA-256 hash proves integrity only — anyone can compute a SHA-256 of anything, so a bare hash alongside a message proves nothing about who sent it. HMAC combines a hash function with a secret key to prove both integrity and authenticity. That's why webhook signatures, JWT's HS256 algorithm, and API request signing use HMAC-SHA256, not raw SHA-256.
Are MD5 and SHA-1 actually broken, or just old?
Actually broken. MD5's collision resistance broke in 2004; SHA-1's broke in practice on February 23, 2017, with Google and CWI Amsterdam's "SHAttered" attack. Both still compute a hash for any input — they're broken in the specific sense that an attacker can now deliberately construct two different inputs sharing one hash, which defeats signatures and integrity proofs. Neither is safe for new security-relevant work.
// try it yourself
Compute MD5, SHA-1, SHA-256, and SHA-512 of any text with the Hash Generator — computed locally via the browser's Web Crypto API, nothing sent to any server.
Every hash function you'll meet falls into one of two categories with opposite design goals. Fast/general-purpose hashes (MD5, SHA-1, SHA-256, SHA-512) are built to process gigabytes per second — perfect for checksums and integrity checks, and exactly wrong for passwords, because an attacker with a stolen hash database can also compute billions of guesses per second. Password KDFs (bcrypt, scrypt, Argon2) are deliberately, tunably slow — that's the entire feature. The same property that makes SHA-256 great at hashing a 2GB file makes it bad at protecting a password.