MD5 has been declared dead more times than I can count, yet it's still sitting in production code all over the place — cache keys, checksums, ETags, dedup logic. The problem isn't MD5 itself. It's that people reach for it in situations where it's genuinely dangerous. So let's sort out the difference.
What MD5 actually is
MD5 (Message-Digest Algorithm 5) is a cryptographic hash function that takes any input and produces a fixed 128-bit output, usually written as a 32-character hexadecimal string. Same input in, same hash out, every single time. Change one byte and the output looks completely different.
A few properties worth remembering:
- Deterministic — the same string always maps to the same digest.
- Fixed length — a 2-byte file and a 2 GB file both produce 32 hex characters.
- Fast — it's cheap to compute, which is both a feature and, as we'll see, a security flaw.
- One-way — you can't reverse the hash back into the original input.
That last point has caveats. For small or common inputs, rainbow tables and brute force make "one-way" mostly theoretical.
When MD5 is perfectly fine
MD5 was broken for security purposes years ago, but it remains a solid, fast checksum for non-adversarial cases. These are legitimate uses:
- Cache keys and content addressing — hashing a request URL or a config blob to get a stable, short key.
- Deduplication — spotting identical files or records where nobody is actively trying to trick you.
- Integrity checks against accidental corruption — verifying a download didn't get mangled in transit. (Not tampering — more on that below.)
- Quick fingerprinting — generating ETags or build artefact IDs.
The common thread: there's no attacker trying to produce a collision on purpose. If you just need a fast, consistent fingerprint of some data, MD5 does the job and it's everywhere, so interop is easy.
When MD5 will absolutely burn you
Do not use MD5 for anything security-sensitive. Specifically:
Password storage
This is the big one. MD5 is far too fast — a modern GPU can compute billions of MD5 hashes per second, which means even plain brute force against hashed passwords is practical. Use a purpose-built password hashing function instead: bcrypt, scrypt, or Argon2. Those are deliberately slow and salted.
Digital signatures or tamper detection against an adversary
MD5 is vulnerable to collision attacks. Researchers have constructed two different inputs that produce the same MD5 digest, and this has been used to forge certificates in the real world. If someone benefits from faking the data behind a hash, MD5 can't protect you. Reach for SHA-256 or SHA-3.
Anything compliance-related
Standards like PCI-DSS, FIPS, and most modern security audits treat MD5 as deprecated. If an auditor sees it protecting sensitive data, expect a finding.
A quick worked example
Say you're building a simple file-level dedup system. You want to detect when two uploads are byte-for-byte identical so you only store one copy. That's a non-adversarial integrity use — MD5 is reasonable here.
In Node.js:
const crypto = require('crypto');
const fs = require('fs');
function md5File(path) {
const hash = crypto.createHash('md5');
const data = fs.readFileSync(path);
hash.update(data);
return hash.digest('hex');
}
console.log(md5File('report.pdf'));
// e.g. 9e107d9d372bb6826bd81d3542a419d6Hashing a short string is just as direct:
const crypto = require('crypto');
const digest = crypto
.createHash('md5')
.update('The quick brown fox jumps over the lazy dog')
.digest('hex');
console.log(digest);
// 9e107d9d372bb6826bd81d3542a419d6Python is similarly short:
import hashlib
digest = hashlib.md5(b"The quick brown fox jumps over the lazy dog").hexdigest()
print(digest)
# 9e107d9d372bb6826bd81d3542a419d6When I'm debugging or just need to confirm a value by hand, I don't write a script — I paste the string into an online tool. The MD5 Hash Generator gives you the digest instantly, which is handy for sanity-checking that your code produces the hash you expect or comparing against a vendor's published checksum.
Common gotchas
Encoding matters
Hashing "café" as UTF-8 gives a different result than Latin-1. If your hash doesn't match between two systems, encoding mismatch is the first thing to check. Always be explicit about how strings are turned into bytes before hashing.
Trailing whitespace and line endings
A file with Windows \r\n line endings hashes differently from the same file with Unix \n. This trips people up constantly when verifying checksums across platforms. The byte stream has to be identical, invisible characters included.
Hex case
Some libraries output uppercase hex, some lowercase. The underlying hash is identical, but a naive string comparison will fail. Normalise case before comparing.
Don't confuse hashing with encryption
Hashing is one-way; there's no "decrypt" step. If someone asks you to "decrypt an MD5 hash," what they actually mean is cracking it via lookup or brute force — and the fact that this is feasible for common inputs is exactly why you don't use MD5 for secrets.
Salting won't save passwords
People sometimes think adding a salt makes MD5 safe for passwords. It stops precomputed rainbow tables, sure, but it does nothing about MD5's raw speed. A salted MD5 password hash is still crackable far too fast. Salt is necessary but not sufficient — the slow algorithm is the point.
The short version
Treat MD5 as a fast, convenient fingerprinting tool for data you control and nobody's attacking. Cache keys, dedup, catching accidental corruption — all fine. The moment security, secrets, or a motivated attacker enters the picture, switch to SHA-256 for integrity and Argon2 or bcrypt for passwords. Knowing which bucket your use case falls into is the whole game.
Photo by lesha tuman on Unsplash