SHA-256 vs MD5: Which Hash Function Should You Use?
MD5 and SHA-1 are dead as security tools — collision attacks exist, and GPU clusters can compute billions of hashes per second. SHA-256 is today's minimum for integrity and digital signatures. But even SHA-256 is the wrong choice when you are hashing passwords. Here is the full picture.
What a hash function does
A cryptographic hash function takes arbitrary-length input and produces a fixed-length digest. Three properties define a secure hash:
- Pre-image resistance — given a digest, you cannot find the original input.
- Second pre-image resistance — given an input, you cannot find a different input with the same digest.
- Collision resistance — you cannot find any two inputs that produce the same digest.
MD5 fails on collision resistance. SHA-1 is practically broken for collision attacks. SHA-256 passes all three for currently known attacks.
MD5 — fast and broken
MD5 produces a 128-bit (32 hex character) digest and is extremely fast — around 10 GB/s on modern hardware. That speed is a liability for security applications: an attacker can precompute rainbow tables or brute-force password hashes rapidly.
The practical verdict: MD5 collisions are trivially producible. Two different files can share the same MD5 hash. A certificate authority was forged using MD5 collision attacks in 2008. Do not use MD5 for anything security-sensitive.
MD5 is still fine for non-security use cases: checksums for detecting accidental corruption (not tampering), cache keys, deduplication identifiers, and content-addressable storage where an adversary cannot craft inputs.
SHA-256 — the modern baseline
SHA-256 is part of the SHA-2 family. It produces a 256-bit (64 hex character) digest and has no known practical attacks against its collision resistance. It is the hash function used in TLS certificates, code signing, Bitcoin's proof-of-work, and HMAC-SHA256 (used in JWT signing and HMAC authentication headers).
// SHA-256 in the browser (Web Crypto API)
async function sha256(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
}
const digest = await sha256("hello world");
// → "b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576..."# Python import hashlib digest = hashlib.sha256(b"hello world").hexdigest() print(digest)
SHA-512 and SHA-3
SHA-512 doubles the digest size (512 bits / 128 hex chars) and is faster than SHA-256 on 64-bit hardware due to wider native word operations. Use it when you need a larger digest or are operating on 64-bit architectures. SHA-3 (Keccak) uses an entirely different internal construction (sponge function) and is the correct fallback if SHA-2 were ever broken — but SHA-2 breakage is not a realistic near-term threat.
Comparison table
| Algorithm | Digest size | Collision resistance | Speed | Use today? |
|---|---|---|---|---|
| MD5 | 128-bit | Broken | Very fast | Non-security only |
| SHA-1 | 160-bit | Broken | Fast | No |
| SHA-256 | 256-bit | Strong | Fast | Yes — baseline |
| SHA-512 | 512-bit | Strong | Fast on 64-bit | Yes |
| SHA-3-256 | 256-bit | Strong | Moderate | Yes (future-proof) |
Why you should not hash passwords with SHA-256
General-purpose hash functions are designed to be fast. Password hashing needs to be deliberately slow to resist brute-force attacks. Use a purpose-built password hashing function:
- bcrypt — work factor configurable; well-supported across languages; 72-byte input limit.
- scrypt — memory-hard; harder to parallelize on ASICs.
- Argon2id — winner of the Password Hashing Competition; recommended by OWASP; tunable memory, time, and parallelism.
// Node.js — argon2 package
const argon2 = require("argon2");
const hash = await argon2.hash("user_password");
const valid = await argon2.verify(hash, "user_password"); // trueHMAC — keyed hashing for authentication
If you want to verify that a message came from someone who knows a secret key, use HMAC (Hash-based Message Authentication Code). HMAC-SHA256 is used in AWS Signature V4, JWT HS256, and webhook verification. A plain SHA-256 hash without a key does not authenticate the sender.
Try it yourself
Use the free browser-based Hash Generator on DevBench — no signup, runs entirely in your browser.
Open Hash Generator