What is a Hash Function?
A hash function is a mathematical algorithm that takes input of any size and produces a fixed-length output (hash). Itβs a βone-way functionβ - easy to compute forward, but practically impossible to reverse.
Basic Properties
1. Deterministic Same input always produces same output:
"hello" β 5d41402abc4b2a76b9719d911017c592
"hello" β 5d41402abc4b2a76b9719d911017c592 // Always the same
2. Fixed Length Regardless of input size, same output length:
MD5("hi") β 49f68a5c8493ec2c0bf489821c21fc3b (32 hex chars)
MD5("hello") β 5d41402abc4b2a76b9719d911017c592 (32 hex chars)
MD5("very long text...") β ... (32 hex chars)
3. Avalanche Effect Minimal change in input causes total change in output:
MD5("hello") β 5d41402abc4b2a76b9719d911017c592
MD5("hallo") β 4d186321c1a7f0f354b297e8914ab240 // Completely different!
4. Fast to Compute Hash functions are designed to be fast.
Hash Algorithms
MD5 (Message Digest 5)
Output: 128 bits (32 hex characters)
MD5("password") β 5f4dcc3b5aa765d61d8327deb882cf99
Created: 1991 by Ronald Rivest
Status: β BROKEN - Do NOT use for security
Why broken?
- Collision attacks possible since 2004
- Can generate two different inputs with same hash
- Can be cracked in minutes with modern hardware
When can MD5 still be used?
// β
OK: Non-security checksums
const fileChecksum = md5(fileContent);
// β
OK: Cache keys
const cacheKey = md5(url + params);
// β NEVER: Passwords or security-related
const passwordHash = md5(password); // DANGEROUS!
SHA-1 (Secure Hash Algorithm 1)
Output: 160 bits (40 hex characters)
SHA1("password") β 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
Created: 1995 by NSA
Status: β DEPRECATED - Phase out
Why deprecated?
- Theoretical collision attacks 2005
- Practical collisions demonstrated 2017 (SHAttered attack)
- Git still uses SHA-1 but migrating to SHA-256
Usage today:
// β οΈ Legacy system - phase out
if (isLegacySystem) {
hash = sha1(data); // Plan migration to SHA-256
}
// β New systems - do NOT use
const newHash = sha1(password); // Use SHA-256+ instead
SHA-256 (SHA-2 family)
Output: 256 bits (64 hex characters)
SHA256("password") β 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
Created: 2001 by NSA
Status: β SECURE - Recommended
Advantages:
- No known practical attacks
- Used in Bitcoin and blockchain
- NIST standard
- Balance between security and performance
Usage:
// β
File integrity
const fileHash = sha256(fileContent);
// β
Digital signatures
const signature = sign(sha256(message), privateKey);
// β
Blockchain
const blockHash = sha256(sha256(blockHeader)); // Bitcoin double-SHA256
// β οΈ Passwords - use bcrypt/argon2 instead
const passwordHash = sha256(password); // OK but bcrypt is better
SHA-512 (SHA-2 family)
Output: 512 bits (128 hex characters)
SHA512("password") β b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
Created: 2001 by NSA
Status: β SECURE - Extra strong
Advantages:
- Stronger security than SHA-256
- Faster on 64-bit systems
- Larger hash = less collision risk
Disadvantages:
- Double the output length
- Slower on 32-bit systems
- Often overkill for most use cases
When to use SHA-512:
// β
Extra sensitive data
const topSecretHash = sha512(sensitiveData);
// β
Long-term archiving
const archiveHash = sha512(document);
// β
HMAC for tokens
const token = hmacSha512(data, secretKey);
// β When size matters (URLs, databases)
const shortHash = sha256(data); // Better choice
SHA-3 (Keccak)
Output: Variable (224, 256, 384, 512 bits)
Created: 2015 (NIST competition winner)
Status: β SECURE - New standard
Differences from SHA-2:
- Completely different internal structure (sponge construction)
- Not based on Merkle-DamgΓ₯rd
- Theoretically more resistant to certain attacks
Usage:
// β
When SHA-2 is not enough
const futureProofHash = sha3_256(data);
// β
Ethereum uses Keccak256
const ethereumHash = keccak256(data);
Why not more popular?
- SHA-256 is still secure
- SHA-3 is slower in many implementations
- Less ecosystem support
Comparison
| Algorithm | Output Size | Speed | Security | Use Case |
|---|---|---|---|---|
| MD5 | 128 bit | β‘β‘β‘ Fastest | β Broken | Checksums (non-security) |
| SHA-1 | 160 bit | β‘β‘ Fast | β οΈ Deprecated | Legacy, Git |
| SHA-256 | 256 bit | β‘ Medium | β Secure | Recommended standard |
| SHA-512 | 512 bit | β‘ Medium | β Very secure | Extra security |
| SHA-3 | Variable | π Slower | β Future-proof | New systems |
Practical Examples
1. File Integrity Check
// Download file and verify integrity
async function downloadAndVerify(url, expectedHash) {
const response = await fetch(url);
const buffer = await response.arrayBuffer();
const hash = await crypto.subtle.digest("SHA-256", buffer);
const hashHex = Array.from(new Uint8Array(hash))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
if (hashHex !== expectedHash) {
throw new Error("File integrity check failed!");
}
return buffer;
}
// Usage
await downloadAndVerify(
"https://example.com/file.zip",
"abc123..." // SHA-256 hash from source
);
2. Git Commit Hash
# Git uses SHA-1 (migrating to SHA-256)
git log --oneline
# a1b2c3d Fix bug in login
# e4f5g6h Add new feature
# Commit hash is SHA-1 of:
# - Tree hash
# - Parent commit hash
# - Author info
# - Commit message
# - Timestamp
3. Bitcoin Mining
// Bitcoin mining = find hash starting with zeros
function mine(blockHeader, difficulty) {
let nonce = 0;
const target = "0".repeat(difficulty);
while (true) {
const hash = sha256(sha256(blockHeader + nonce));
if (hash.startsWith(target)) {
return { nonce, hash };
}
nonce++;
}
}
// Find hash starting with 4 zeros
const result = mine("Block data...", 4);
// { nonce: 182394, hash: "0000abc123..." }
4. Password Verification (BAD METHOD)
// β NEVER use SHA for passwords directly!
function badPasswordHash(password) {
return sha256(password);
}
// Problems:
// 1. Too fast - 1 billion attempts per second
// 2. Rainbow tables
// 3. No salt
// β
Use bcrypt/argon2 instead
import bcrypt from "bcrypt";
async function goodPasswordHash(password) {
const salt = await bcrypt.genSalt(12);
return await bcrypt.hash(password, salt);
}
5. Cache Keys
// β
Good use of SHA-256 for cache
function getCacheKey(url, params) {
const data = JSON.stringify({ url, params });
return sha256(data);
}
const cacheKey = getCacheKey("/api/users", { page: 1, limit: 10 });
// "a1b2c3d4..."
cache.get(cacheKey);
Hash vs Encryption
| Hash | Encryption |
|---|---|
| One-way | Two-way |
| Fix output size | Variable output |
| Fast | Slower |
| Deterministic | Uses keys |
| Integrity check | Confidentiality |
Hash:
const hash = sha256("secret");
// "2bb80..."
// Cannot be recovered to "secret"
Encryption:
const encrypted = encrypt("secret", key);
// "x7j9k..."
const decrypted = decrypt(encrypted, key);
// "secret" β
Can be decrypted
HMAC (Hash-based Message Authentication Code)
Combines hash with a secret key:
function hmac(message, secretKey) {
const hash1 = sha256(
(secretKey ^ opad) + sha256((secretKey ^ ipad) + message)
);
return hash1;
}
// Verifies both integrity AND authenticity
const signature = hmac("message", "secret-key");
Usage:
- JWT tokens
- API signatures (AWS, etc.)
- Webhook verification
// Verify webhook from GitHub
const signature = req.headers["x-hub-signature-256"];
const payload = req.body;
const secret = process.env.WEBHOOK_SECRET;
const expectedSignature =
"sha256=" +
crypto
.createHmac("sha256", secret)
.update(JSON.stringify(payload))
.digest("hex");
if (signature !== expectedSignature) {
throw new Error("Invalid signature");
}
Rainbow Tables & Salt
Problem:
// Same password = same hash
sha256("password123") β "ef92b..."
sha256("password123") β "ef92b..." // Same!
// Rainbow table: pre-computed hashes
// "password123" β "ef92b..."
// "123456" β "8d969..."
// ...millions more...
Solution: Salt
// Add random data before hash
const salt = generateRandomBytes(16);
const hash = sha256(password + salt);
// Save both salt and hash
db.save({
salt: salt,
hash: hash,
});
// Now same password has different hash
sha256("password123" + "abc") β "123..."
sha256("password123" + "xyz") β "789..." // Different!
Thatβs why: Use bcrypt which does this automatically!
Choosing the Right Algorithm
For File Integrity
β
SHA-256; // Best balance
β
SHA-512; // Extra paranoid
β MD5; // Only for non-security
For Passwords
β
bcrypt; // Recommended
β
argon2; // More modern, stronger
β
scrypt; // Also good
β SHA-256; // Too fast, no salt management
β MD5; // NEVER!
For Digital Signatures
β
SHA-256; // Standard
β
SHA-512; // Extra security
β
SHA-3; // Future-proof
For Git/Blockchain
β
SHA-256; // Bitcoin, modern Git
β οΈ SHA-1; // Legacy Git (migrating)
For Cache Keys
β
SHA-256; // Good balance
β
MD5; // OK for cache
β
SHA-1; // OK for cache
Performance
Benchmark (1 billion operations):
MD5: ~2 seconds β‘β‘β‘
SHA-1: ~3 seconds β‘β‘
SHA-256: ~4 seconds β‘
SHA-512: ~3 seconds β‘ (on 64-bit)
SHA-3: ~6 seconds π
For most applications, speed doesnβt matter!
Security Best Practices
1. Use the Right Tool
// β Wrong tool
const passwordHash = md5(password);
// β
Right tool
const passwordHash = await bcrypt.hash(password, 12);
2. Stay Updated
// β οΈ Being phased out
if (usingLegacySHA1) {
planMigrationToSHA256();
}
3. Combine with Salt
// β Without salt
const hash = sha256(password);
// β
With salt
const salt = crypto.randomBytes(16);
const hash = sha256(password + salt);
4. Iterate for Passwords
// β One iteration
const hash = sha256(password);
// β
Many iterations (bcrypt does this)
let hash = password;
for (let i = 0; i < 10000; i++) {
hash = sha256(hash);
}
Tools
Test different hash algorithms:
- Hash Generator - Test MD5, SHA-1, SHA-256, SHA-512
- Bcrypt Generator - Secure password hashing
- Password Strength Checker - Validate password strength
Conclusion
For new projects:
- β SHA-256 - Standard for file integrity, signatures
- β bcrypt/argon2 - ALWAYS for passwords
- β HMAC-SHA256 - For authentication tokens
Avoid:
- β MD5 - Only non-security use cases
- β SHA-1 - Being phased out, plan migration
- β SHA-256 for passwords - Use bcrypt instead
Remember: Hash β Encryption β Encoding
Choose the right tool for the job!