Security · 4 min read
How Hashing Works: MD5, SHA-1, SHA-256 and Why Two Are Broken
Two documents, the same MD5 hash, produced on a laptop in seconds. Here is what that means and what it does not.
A hash function takes input of any length and returns a fixed-length value. SHA-256 always returns 256 bits, whether you feed it one byte or a terabyte. The same input always gives the same output; there is no key and no randomness.
A cryptographic hash function makes three additional promises:
- Given a hash, you cannot find an input that produces it (preimage resistance).
- Given an input, you cannot find a different input with the same hash (second preimage resistance).
- You cannot find any two inputs sharing a hash (collision resistance).
The third is the weakest and always breaks first, because the attacker gets to choose both inputs.
The avalanche effect
Change one bit of input and roughly half the output bits should flip. SHA-256 of hello:
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-256 of hellp: one letter later in the alphabet:
7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a
No visible relationship. This is what makes hashes useful as fingerprints: any change, however small, is loud.
Why collisions are cheaper than they look
An n-bit hash has 2n possible outputs, so you might expect finding a collision to take about 2n attempts. It takes about 2n/2, because of the birthday paradox. In a room of 23 people there is a better-than-even chance two share a birthday, even though there are 365 of them.
So MD5, at 128 bits, needs roughly 264 operations for a generic collision, already borderline. Its actual break was far worse than that.
MD5: broken in 2004, still everywhere
Wang Xiaoyun and colleagues published a differential attack in 2004 that found MD5 collisions in hours rather than the theoretical 264. The cost has since fallen to seconds on ordinary hardware.
The consequences were not theoretical. In 2008 a research team used an MD5 chosen-prefix collision to obtain a rogue CA certificate that browsers would trust. In 2012, the Flame malware used an MD5 collision against a Microsoft certificate to make itself appear to be a signed Windows Update, one of the more consequential cryptographic attacks ever deployed in the wild.
MD5 remains acceptable for non-adversarial uses: cache keys, deduplication, detecting accidental corruption. It is unacceptable anywhere an attacker influences the input.
SHA-1: deprecated for years, then demonstrated
Theoretical attacks appeared in 2005. Everyone knew it was coming. It still took until February 2017 for Google and CWI Amsterdam to publish SHAttered: two PDF files with different visible content and an identical SHA-1 hash.
The computation cost roughly 6,500 CPU-years and 110 GPU-years, about $110,000 of cloud compute at the time. By 2020 the chosen-prefix variant had brought this down to around $45,000, which puts it within reach of well-funded attackers rather than just nation states.
SHA-1 is now removed from TLS certificates, rejected by modern browsers, and being migrated away from in Git, which used it for content addressing rather than security, but where a collision is still a problem.
SHA-256 and the SHA-2 family
SHA-2 is the current workhorse: SHA-224, SHA-256, SHA-384 and SHA-512, published by NIST in 2001. There are no practical attacks against the full-round versions after two decades of scrutiny. Best-known results reach about 31 of 64 rounds for SHA-256: a comfortable margin.
SHA-3 (Keccak, standardised 2015) exists not because SHA-2 is weak but because it uses a completely different internal construction. If a structural break against the Merkle-Damgård design used by MD5, SHA-1 and SHA-2 were ever found, SHA-3’s sponge construction would be unaffected. It is insurance, not a replacement.
| Function | Bits | Status | Use for |
|---|---|---|---|
| MD5 | 128 | Broken (2004) | Checksums only, never security |
| SHA-1 | 160 | Broken (2017) | Legacy compatibility only |
| SHA-256 | 256 | Secure | General purpose default |
| SHA-512 | 512 | Secure | Faster than SHA-256 on 64-bit hardware |
| SHA-3 | 224 to 512 | Secure | Structural diversity |
| BLAKE3 | 256+ | Secure | Very high throughput |
Passwords need the opposite of a fast hash
Every property above optimises for speed. For password storage that is precisely wrong.
If your database leaks and passwords were stored as SHA-256, an attacker with a consumer GPU can test billions of candidates per second. Common passwords fall in milliseconds; an eight-character password from a realistic character set falls in hours.
Password hashing functions are deliberately expensive:
- bcrypt (1999) has a tunable cost factor. Each increment doubles the work. Cost 12 is a reasonable current setting; cost 10 is the common default and increasingly light.
- scrypt (2009) adds a memory requirement, which frustrates GPU and ASIC attacks that have plenty of parallel compute but limited fast memory per core.
- Argon2 (2015) won the Password Hashing Competition and has independent time, memory and parallelism parameters. Argon2id is the current recommendation for new systems.
All three include a random salt per password, which is what defeats rainbow tables: identical passwords produce different hashes, so precomputation buys the attacker nothing.
The bcrypt generator lets you see the cost factor’s effect directly. Raise it and watch how long the operation takes in your own browser. That delay is the entire security mechanism.
Practical rules
- Default to SHA-256 for integrity, fingerprints and signatures.
- Never use a general-purpose hash for passwords. Use Argon2id, or bcrypt if that is what your stack supports.
- Use HMAC, not a bare hash, for authentication.
hash(secret + message)is vulnerable to length-extension against Merkle-Damgård functions; HMAC is not. - Compare hashes in constant time. A short-circuiting
==leaks information through timing. - Do not truncate below 128 bits, and remember that truncating halves your collision resistance in the exponent.
Common questions
Is MD5 safe for checking file integrity?
Against accidental corruption, yes: a truncated download or a flipped bit will change the hash. Against a deliberate attacker, no. Someone who controls the file can construct a different file with the same MD5. If the threat model includes anyone malicious, use SHA-256.
Can a hash be reversed?
Not by inverting the function. It destroys information by design. But it can be brute-forced: hash candidate inputs until one matches. For short or common inputs this is trivial, which is exactly why unsalted password hashes fall to rainbow tables. The one-way property protects the function, not a low-entropy input.
Why is SHA-256 wrong for passwords?
Because it is fast, and speed is the attacker’s ally. A modern GPU computes billions of SHA-256 hashes per second, so a leaked hash database can be attacked at enormous rates. Password hashes need to be deliberately slow and memory-hard: bcrypt, scrypt or Argon2. Same maths, opposite design goal.