Cryptographic Hash Functions Explained: MD5, SHA-256 & Security
Cryptographic hash functions are fundamental building blocks of modern security. They power password storage, digital signatures, blockchain, file integrity verification, and much more. This guide explains how hash functions work, compares the most common algorithms, and covers security best practices you need to know.
What Is a Hash Function?
A cryptographic hash function takes an input of any size and produces a fixed-length output (the "hash" or "digest"). A good hash function has these properties:
- Deterministic: The same input always produces the same hash
- Fast to compute: Generating a hash is efficient
- Pre-image resistant: Given a hash, it's infeasible to find the original input
- Collision resistant: It's infeasible to find two different inputs with the same hash
- Avalanche effect: A tiny change in input produces a completely different hash
# SHA-256 avalanche effect demonstration
echo -n "hello" | sha256sum
# 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
echo -n "hello!" | sha256sum
# ce06092fb948d9ffac7d1a376e404b26b7575bcc11ee05a4615fef4fec3a308b
# Completely different output from just adding "!"
Common Hash Algorithms
MD5 (128-bit)
Designed by Ronald Rivest in 1991, MD5 produces a 128-bit (32 hex character) digest. It's fast but cryptographically broken — collision attacks have been practical since 2004.
# Command line
echo -n "Hello" | md5sum
# 8b1a9953c4611296a827abf8c47804d7
# Python
import hashlib
hashlib.md5(b"Hello").hexdigest()
# '8b1a9953c4611296a827abf8c47804d7'
SHA-1 (160-bit)
SHA-1 produces a 160-bit hash. Google demonstrated a practical collision (SHAttered) in 2017. Major browsers and certificate authorities have deprecated SHA-1. Avoid it for new projects.
SHA-256 (256-bit)
Part of the SHA-2 family designed by the NSA, SHA-256 is currently the industry standard for general-purpose hashing. It produces a 256-bit (64 hex character) digest and has no known practical attacks.
// JavaScript (Web Crypto API)
async function sha256(message) {
const data = new TextEncoder().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('');
}
await sha256("Hello");
// "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"
# Python
import hashlib
hashlib.sha256(b"Hello").hexdigest()
# '185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969'
SHA-3 (Variable length)
SHA-3 (Keccak) was standardized in 2015 as an alternative to SHA-2. It uses a completely different internal structure (sponge construction) and provides an independent security fallback if SHA-2 is ever compromised.
Hash Algorithm Comparison
- MD5: 128-bit output, broken — use only for checksums
- SHA-1: 160-bit output, deprecated — avoid for new work
- SHA-256: 256-bit output, secure — the current standard
- SHA-512: 512-bit output, secure — faster on 64-bit CPUs
- SHA-3-256: 256-bit output, secure — independent alternative to SHA-2
Password Hashing: Why SHA-256 Isn't Enough
General-purpose hash functions like SHA-256 are too fast for password hashing. An attacker with a GPU can compute billions of SHA-256 hashes per second. Instead, use purpose-built password hashing functions:
- bcrypt: Time-tested, configurable work factor, built-in salt
- scrypt: Memory-hard, resists GPU attacks
- Argon2: Winner of the Password Hashing Competition (2015), best choice for new projects
# Python: password hashing with bcrypt
import bcrypt
password = b"my_secure_password"
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)
# b'$2b$12$LJ3m4ys3Lz0YqB4K5Vh...'
# Verify
bcrypt.checkpw(b"my_secure_password", hashed) # True
bcrypt.checkpw(b"wrong_password", hashed) # False
Real-World Applications
- File integrity: Verify downloads haven't been tampered with by comparing SHA-256 checksums
- Digital signatures: Hash the message first, then sign the hash with a private key
- Blockchain: Bitcoin uses double SHA-256 for proof-of-work mining
- HMAC: Hash-based Message Authentication Code combines a secret key with a hash for message authentication
- Content addressing: Git uses SHA-1 (migrating to SHA-256) to identify commits and objects
# Verify a file download
sha256sum downloaded_file.tar.gz
# Compare output with the published checksum
# HMAC in Python
import hmac, hashlib
key = b"secret_key"
message = b"important data"
mac = hmac.new(key, message, hashlib.sha256).hexdigest()
print(mac) # "a]..."
🛠️ Generate Hashes Online
Open Hash Tool →Further Reading
- JWT Complete Guide — JWTs use HMAC-SHA256 or RSA-SHA256 for signing
- Base64 Encoding Explained — Hash digests are often Base64-encoded for transport
- Regex Tutorial — Validate hash string formats with regular expressions