MD5, SHA-256, bcrypt: Which Hash Do You Actually Need?
A no-nonsense breakdown of hash functions. What each one does, when to use it, and the one you should never use for passwords.
A hash function takes input of any size and produces a fixed-size output. It's deterministic (same input always gives the same output), one-way (you can't reverse it to get the original input), and even a tiny change in input produces a completely different hash. That's the whole contract.
The question isn't whether to use hashing -- it's which algorithm to pick. They have very different properties and very different appropriate use cases.
MD5: Fast, Broken, Still Useful
MD5 produces a 128-bit (32-character hex) hash. It's fast. Really fast. That used to be a selling point; now it's a liability for anything security-related.
In 2004, researchers demonstrated MD5 collision attacks -- creating two different inputs that produce the same hash. In 2012, the Flame malware exploited an MD5 collision to forge a Microsoft code-signing certificate. The algorithm is cryptographically broken.
Where MD5 still makes sense
- Cache keys: You need a fast, short identifier for a piece of data. Collision resistance doesn't matter here because you're not defending against an attacker.
- Deduplication: Quick check for identical files. If the hashes match, do a byte-for-byte comparison to confirm.
- Checksums in non-adversarial contexts: Verifying a download wasn't corrupted in transit (not that it wasn't tampered with).
Generate one with the MD5 hash generator. Just don't use it for passwords or security.
SHA-256: The Workhorse
SHA-256 is part of the SHA-2 family and produces a 256-bit (64-character hex) hash. No known practical attacks exist against it. It's the default choice when you need a cryptographic hash.
Where it shows up
- Git: Every commit, tree, and blob is identified by its SHA-256 hash (Git migrated from SHA-1).
- Bitcoin: Block hashing, proof of work, transaction IDs -- all SHA-256.
- File integrity: Software distributions publish SHA-256 checksums so you can verify downloads haven't been tampered with.
- API request signing: AWS Signature V4 uses HMAC-SHA256 to sign every API request.
- Digital certificates: TLS certificates use SHA-256 in their signature algorithms.
# Verify a downloaded file $ sha256sum ubuntu-24.04-desktop-amd64.iso a1b2c3d4e5f6... ubuntu-24.04-desktop-amd64.iso # Compare against the published checksum $ echo "a1b2c3d4e5f6... ubuntu-24.04-desktop-amd64.iso" | sha256sum --check ubuntu-24.04-desktop-amd64.iso: OK
Try it yourself with the SHA-256 hash generator.
SHA-512: More Bits, Marginal Gains
SHA-512 outputs 512 bits (128 hex characters). It's in the same SHA-2 family as SHA-256. On 64-bit processors, SHA-512 is actually faster than SHA-256 because it operates on 64-bit words natively.
The collision resistance is higher (2^256 vs 2^128 for SHA-256), but SHA-256's collision resistance is already far beyond what's practically attackable. You'll see SHA-512 in some certificate chains, DNSSEC implementations, and protocols that want the extra margin.
For most applications, SHA-256 and SHA-512 are interchangeable. Pick SHA-512 if your spec requires it or if you're on a 64-bit system and want slightly better throughput. Generate hashes with the SHA-512 hash generator.
SHA-1: Deprecated, Still Haunting Us
SHA-1 produces 160-bit hashes. Google demonstrated a practical collision attack (SHAttered) in 2017 by creating two different PDFs with the same SHA-1 hash.
Chrome stopped accepting SHA-1 certificates in 2017. Git originally used SHA-1 for object IDs and has been migrating to SHA-256. Microsoft, Mozilla, and Apple all dropped SHA-1 certificate support.
You'll still encounter SHA-1 in legacy systems, older Git repositories, and some fingerprinting use cases. If you're working with one of these systems, the SHA-1 hash generator is useful for debugging. For anything new, use SHA-256.
bcrypt: The Password Hash
Every algorithm above is designed to be fast. That's terrible for password hashing. A fast hash means an attacker can try billions of guesses per second.
bcrypt is purpose-built to be slow. It has a configurable cost factor (work factor) that controls how many iterations the algorithm runs. Bump the cost by 1, and hashing takes twice as long. A cost of 12 takes roughly 250ms on modern hardware -- imperceptible to a user logging in, devastating to an attacker trying billions of passwords.
// bcrypt output format: $2b$12$LJ3m4ys3Lg2VHjOMYKFfpeGNSBCFEm/BKOH1xOe2GWa/WRLrtgPi // Breaking it down: // $2b$ - bcrypt version // $12$ - cost factor (2^12 = 4096 iterations) // LJ3m4ys3Lg2VHjOMYKFfpe - 22-char salt (Base64-encoded) // GNSBCFEm/BKOH1xOe2GWa/WRLrtgPi - 31-char hash (Base64-encoded)
bcrypt also handles salting automatically. Each hash includes a random salt, so two users with the same password get different hashes. No rainbow table attacks.
The rules for password hashing
- Use bcrypt, scrypt, or Argon2. Never MD5 or SHA-256 for passwords.
- Set the cost factor as high as your login latency budget allows (12+ is a good start).
- Never store passwords in plain text. Never store them with reversible encryption.
- Don't roll your own password hashing. Use your language's established library.
Test bcrypt hashing and verification with the bcrypt hash generator.
Comparison Table
| Algorithm | Output | Speed | Use For | Avoid For |
|---|---|---|---|---|
| MD5 | 128-bit | Very fast | Cache keys, checksums, dedup | Passwords, security, signatures |
| SHA-1 | 160-bit | Fast | Legacy system compat only | Anything new |
| SHA-256 | 256-bit | Fast | Integrity, signatures, general crypto | Password hashing |
| SHA-512 | 512-bit | Fast (faster on 64-bit) | Certs, DNSSEC, extra margin | Password hashing |
| bcrypt | 184-bit | Intentionally slow | Password storage | Checksums, fast lookups |
The Short Version
Storing passwords? bcrypt. Verifying file integrity or signing data? SHA-256. Working with a legacy system? Check what it requires. Need a fast, non-security hash for caching or dedup? MD5 is fine.
The one thing you should never do: use a general-purpose hash (MD5, SHA-*) for password storage. The speed that makes them good for checksums makes them terrible for passwords. And if you're ever unsure whether something is actually encrypted or just encoded, paste it into a Base64 decoder -- you might be surprised at what comes back in plain text.