“MD5 is broken” is true but incomplete. It's broken specifically for collision resistance — researchers demonstrated in 2004 that you can construct two different inputs that hash to the same MD5 output, and by now doing so is fast and well-documented. That matters enormously if you're using a hash to prove something wasn't tampered with. It matters much less if you're just using a hash to detect accidental corruption, like verifying a file downloaded correctly.
Where each one is still fine
- MD5 — checking a downloaded file's integrity against a published checksum, generating a cache key from non-sensitive input, deduplicating identical blobs. Nobody is trying to forge a collision against your CDN cache key.
- SHA-256 — anything where an adversary might benefit from forging a match: digital signatures, blockchain, verifying software hasn't been tampered with, Git commit hashes (technically SHA-1 historically, migrating to SHA-256).
Neither one is for passwords
This is the mistake that actually causes damage. MD5 and SHA-256 are both fast by design — that's a feature for checksums and a serious liability for password storage, because fast hashing means an attacker with a leaked hash database can try billions of guesses per second on cheap hardware. Password storage needs a deliberately slow, memory-hard function: bcrypt, scrypt, or argon2. If you see raw SHA-256 (or worse, MD5) used to store passwords in a codebase, that's worth flagging regardless of how everything else looks.
SHA-1 and SHA-512, briefly
SHA-1 sits between the two — theoretically broken (Google demonstrated a practical collision in 2017, the “SHAttered” attack) but still shows up in legacy systems and Git's object model. Treat it like MD5: fine for non-adversarial integrity checks, not for anything security-sensitive. SHA-512 is SHA-256's bigger sibling — same security properties, larger output, marginally slower on 32-bit systems and marginally faster on 64-bit ones. Pick SHA-256 unless you have a specific reason for the larger digest.