MD5 still shows up everywhere — in legacy codebases, npm packages, config examples, and Stack Overflow answers. The question isn't whether you'll encounter it; it's whether you should keep using it. The answer depends entirely on what you're hashing it for. SHA-256, standardized in FIPS 180-4, has no known practical attacks after two decades of cryptanalysis. MD5 had practical collision attacks demonstrated in 2004 and was used to forge a rogue SSL certificate in 2008. Their security profiles are not comparable — but that doesn't mean MD5 is always the wrong choice.
Four properties that make a hash function cryptographic
A hash function takes an input of any size and produces a fixed-length output (the hash or digest). For a hash function to be cryptographically useful, it needs to satisfy four properties:
- Deterministic: same input always produces the same output
- Avalanche effect: a single bit change in input produces a completely different output
- One-way (preimage resistance): computationally infeasible to reverse a hash and recover the input
- Collision resistance: computationally infeasible to find two different inputs that produce the same hash — this is the property MD5 has lost
It's the last property — collision resistance — where MD5 has definitively failed.
MD5: how it fell apart and when that matters
MD5 (Message-Digest Algorithm 5) was designed by Ronald Rivest in 1991. It produces a 128-bit (32 hex character) hash. For over a decade it was the dominant hash function for file integrity verification and digital signatures — and then things went wrong.
The problems emerged in stages. In 1996 researchers found theoretical weaknesses. By 2004, Chinese cryptographers demonstrated practical collision attacks — they could craft two different files that produced the same MD5 hash. In 2008, a team forged a rogue SSL certificate that browsers accepted as legitimate. In 2012, the Flame malware used MD5 collisions to fake a Microsoft code-signing certificate.
MD5 is now considered cryptographically broken for any use that requires collision resistance. It should not be used for digital signatures, certificates, or security-critical integrity verification.
The SHA Family
The Secure Hash Algorithm (SHA) family was developed by the NSA and standardized as Federal Information Processing Standards (FIPS) by NIST. The SHA-2 family (which includes SHA-256) has no known practical attacks:
- SHA-1 (1995): 160-bit output. Practical chosen-prefix collision demonstrated by Google's SHAttered team in 2017. Deprecated for signatures and certificates.
- SHA-256 (2001): 256-bit output, specified in FIPS 180-4. No known practical attacks. The default choice for modern security infrastructure.
- SHA-512 (2001): 512-bit output. Faster than SHA-256 on 64-bit processors for long inputs due to larger block processing.
When MD5 is Still Acceptable
Despite its broken collision resistance, MD5 is still fine for non-security use cases where no adversary is intentionally crafting collisions:
- File deduplication: detecting identical files in a storage system — nobody's crafting collisions to sneak duplicates past you
- Cache keys: generating short fixed-length keys from long query strings or request parameters
- Gravatar: the service uses MD5 hashes of email addresses for profile image URLs — yes, still
- Non-security checksums: detecting accidental corruption (bit rot, transmission errors) where no active attacker is involved
Unpopular opinion: HMAC-SHA256 is overkill for most content-hash use cases. If you're just checking file integrity and don't need tamper resistance, MD5 is still fine and measurably faster. But if you're ever in a situation where an attacker controls the input — use SHA-256.
Why SHA-256 is also wrong for passwords
For password storage, neither MD5 nor SHA-256 is appropriate — even though SHA-256 is fine in other contexts. The reason is speed. Both are designed to be fast. A modern GPU cluster can compute tens of billions of SHA-256 hashes per second. If your password database leaks, an attacker can try every word in a large dictionary plus millions of common patterns in seconds.
Password-specific hashing algorithms are designed to be deliberately slow, with a tunable work factor that increases cost as hardware gets faster:
- bcrypt: the industry standard for web apps since 1999. Cost factor 10-12 doubles computation time with each increment. Still the safest default for most projects.
- Argon2: winner of the Password Hashing Competition (2015). Memory-hard, which defeats GPU and ASIC attacks. The recommendation for new systems.
- PBKDF2: NIST-approved and required by some compliance frameworks (FIPS 140). Uses HMAC-SHA256 iteratively.
// Node.js — password hashing with bcrypt
const bcrypt = require('bcrypt');
// Hash a password (cost factor 12)
const hash = await bcrypt.hash(plainPassword, 12);
// Store 'hash' in the database
// Verify a password on login
const isValid = await bcrypt.compare(plainPassword, storedHash);
// Node.js — Argon2 with the argon2 package
const argon2 = require('argon2');
const hash = await argon2.hash(plainPassword);
const isValid = await argon2.verify(hash, plainPassword);Summary
- Use SHA-256 for file integrity, API request signing, digital signatures, TLS, and any security-critical checksum
- MD5 is acceptable for non-security checksums, cache keys, and deduplication where adversarial collision attacks are not a concern
- Never use MD5 for anything security-critical: signatures, certificates, or any context where an attacker might craft collisions
- Use bcrypt, Argon2, or PBKDF2 for password hashing — not SHA-256, and definitely not MD5