Hashing for Those Who Never Read the RFC: MD5, SHA-1, SHA-256
Each algorithm, its historical flaws, and when it still makes sense to use — without the heavy math.
I was reviewing a legacy API codebase and found passwords being stored with MD5. In 2024. The original dev probably thought it was "encrypted" — and here's the problem: MD5 doesn't encrypt anything, and even for hashing non-sensitive data, the choice of algorithm matters much more than most people imagine.
This post is what you need to know about the three most common algorithms, without going into finite group theory.
What hashing really is
A hash function is a one-way function: given an input, it always produces the same fixed-size output. You can't reverse the output to get back to the original input — at least not directly.
What differentiates one hash algorithm from another is basically: speed, output size, and collision resistance (two different inputs generating the same output).
MD5
Output: 128 bits (32 hex chars).
Created in 1992. Broken by collision in 2004. In 2008, researchers generated a fake CA certificate exploiting MD5 collisions. In 2012, the Flame malware used the same technique.
Never use MD5 for:
- Password hashing (it's too fast — makes brute-force easy)
- Security integrity verification (collisions are computationally feasible)
- Digital signatures
Still makes sense for:
- File checksums in contexts where speed matters and security isn't critical
- Content deduplication IDs (checking if a file already exists in cache)
- Non-cryptographic fingerprinting
echo -n "hello" | md5sum
# 5d41402abc4b2a76b9719d911017c592
SHA-1
Output: 160 bits (40 hex chars).
Developed by the NSA, published in 1995. Practical collision demonstrated by Google in 2017 (the SHAttered attack). Browsers stopped accepting TLS certificates with SHA-1 in 2017.
Never use SHA-1 for:
- TLS/SSL certificates
- Code signing
- Any modern security context
Still appears in:
- Git (uses SHA-1 to identify commits — migrating to SHA-256)
- Some legacy systems for non-critical integrity verification
Git uses SHA-1 as an identifier, not as a security guarantee. Collision resistance matters less when the adversary doesn't control the content of commits.
SHA-256
Output: 256 bits (64 hex chars). Part of the SHA-2 family.
Published in 2001. No known collision. Widely used in TLS, HTTPS, Bitcoin, JWT (HS256 algorithm), and software integrity verification.
Use SHA-256 for:
- Critical file integrity verification
- HMACs (message signatures with a secret key)
- Security checksums in general
Don't use SHA-256 for:
- Password hashing — it's still too fast. Use bcrypt, scrypt, or Argon2 for passwords
const hash = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode('hello')
);
// ArrayBuffer → hex string
const hex = [...new Uint8Array(hash)].map(b => b.toString(16).padStart(2, '0')).join('');
For passwords: none of the three
MD5, SHA-1, and SHA-256 are algorithms that are fast by design. For passwords, you want the opposite: a deliberately slow algorithm that makes brute-force computationally expensive.
Use bcrypt (cost factor >= 12), scrypt (N >= 16384), or Argon2id (the most modern). These algorithms are tunable — you increase the computational cost as hardware gets cheaper.
Testing in practice
To generate and compare hashes of any text with MD5, SHA-1, SHA-256, and others, the Hash Generator processes everything in the browser — without sending anything to a server.
How to choose the right hash
The most important question isn't which algorithm is most famous, but which problem you want to solve. For file integrity, SHA-256 is usually a safe and widely supported choice. For fast checksums without a security objective, algorithms like CRC may be sufficient. For passwords, generic hashing isn't enough: use algorithms with configurable cost, like Argon2, bcrypt, or scrypt.
Never use MD5 or SHA-1 for security. They still appear in legacy systems and can serve to identify duplicates without critical risk, but they should not protect authentication, signing, or integrity against an active attacker.
Checklist before publishing
Confirm whether the hash needs a salt, whether it needs to be reproducible in another system, and whether the output format should be hex or Base64. Also document the input encoding. The string action may generate different results if one system uses UTF-8 and another handles bytes differently.
Use the Hash Generator to compare algorithms, test known inputs, and record examples in the README or API documentation. Real examples prevent regressions when another language or library enters the flow.
- 01 UTF-8 and encoding problems: why accents break and how to fix them What UTF-8 is, code points, mojibake, and why accented text turns to garbage — the mechanism explained end to end.
- 02 MIT, Apache, and GPL Licenses: Practical Differences for Developers MIT, Apache 2.0, and GPL are not the same thing. Understand permissive vs copyleft, patent protection, and compatibility before shipping code to production.