Cryptographic hash functions are one of the most fundamental primitives in software security — yet they're also among the most frequently misused. Using MD5 to hash passwords or SHA-1 for certificate signatures creates vulnerabilities that can be exploited in minutes. This guide gives you a complete mental model and the rules for choosing correctly.
What Is a Hash Function?
A cryptographic hash function takes an input of any length and produces a fixed-size output (the hash, digest, or fingerprint). Four essential properties define a secure hash function:
- Deterministic — the same input always produces the same output
- One-way (pre-image resistance) — you cannot reverse the hash to recover the original input
- Avalanche effect — changing even a single bit in the input produces a completely different hash
- Collision resistance — it must be computationally infeasible to find two different inputs that produce the same hash
SHA-256("hello") → 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-256("hello!") → ce06092fb948d9af09a4da2b3bc0c2b73c36cf58c16a39b0eba78c07e2e4ee6b
↑ Completely different — avalanche effect
Algorithm Comparison
| Algorithm | Output | Speed | Status |
|---|---|---|---|
| MD5 | 128-bit / 32 hex | Fastest | ❌ Broken — collisions trivial since 2004 |
| SHA-1 | 160-bit / 40 hex | Fast | ❌ Broken — SHAttered attack 2017 |
| SHA-256 | 256-bit / 64 hex | Fast | ✅ Secure — current industry standard |
| SHA-384 | 384-bit / 96 hex | Fast | ✅ Secure |
| SHA-512 | 512-bit / 128 hex | Fastest on 64-bit | ✅ Secure — strongest SHA-2 variant |
Choosing the Right Algorithm
File Integrity Checksums
When verifying a downloaded file or checking if a file has changed between deployments, use SHA-256. It's fast enough to hash gigabyte files in seconds and is the current standard for package managers (npm, pip, Homebrew) and OS package verification.
# Shell: compute and verify SHA-256
sha256sum myfile.tar.gz > myfile.tar.gz.sha256
sha256sum -c myfile.tar.gz.sha256
# Node.js
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
TLS Certificates and Code Signing
All modern TLS certificates use SHA-256 for the signature algorithm (RS256 or ES256). The CA/Browser Forum has mandated SHA-256 minimum since 2015. SHA-1 certificates are rejected by all modern browsers. SHA-512 is equally acceptable but offers minimal practical benefit for certificates.
Password Hashing — Do NOT Use SHA-*
This is the most critical rule: never use a general-purpose hash function (SHA-256, MD5, etc.) for storing passwords. They are designed to be fast, which is exactly wrong for passwords. A GPU can compute billions of SHA-256 hashes per second, making brute-force attacks trivial.
Use a purpose-built password hashing function instead:
| Function | Language / Framework | Default Config |
|---|---|---|
| Argon2id | All (libsodium bindings) | Best — OWASP recommended |
| bcrypt | All platforms | cost=12 minimum |
| scrypt | Node.js built-in, others | N=32768, r=8, p=1 |
HMAC — Keyed Hashing for Authentication
HMAC (Hash-based Message Authentication Code) uses a secret key combined with a hash function to produce a tamper-proof tag. It's used everywhere authentication matters:
- Webhook verification — Stripe, GitHub, Shopify all use HMAC-SHA-256 to sign webhook payloads
- JWT signatures — the HS256 algorithm is HMAC-SHA-256
- AWS API authentication — AWS Signature Version 4 uses HMAC-SHA-256
- Cookie signing — Express.js signed cookies use HMAC
// Node.js — verify a Stripe webhook
const crypto = require('crypto');
function verifyStripeSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// CRITICAL: use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
===. Regular string comparison short-circuits on the first mismatched character, leaking timing information that attackers can exploit to forge signatures. Always use a constant-time comparison function.Hex vs Base64 Output
Hash digests are raw bytes — the encoding is a display choice:
| Encoding | Size | Characters | Best for |
|---|---|---|---|
| Hex | 2× bytes | 0-9, a-f | CLI display, debugging, log files |
| Base64 | 1.33× bytes | A-Z, a-z, 0-9, +/= | HTTP headers, JWT, compact storage |
The Dev Cosmos Hash Generator lets you toggle between hex and Base64 output for all algorithms simultaneously.
Hashing Files
Drag any file onto the Hash Generator's drop zone. It reads the file using the File API and feeds the raw bytes to each algorithm via the Web Crypto API. No upload happens — everything runs in your browser. Useful for:
- Comparing a downloaded binary against a published SHA-256 checksum
- Detecting if a config file changed between environments
- Generating checksums for CI/CD artifact verification
crypto.subtle.digest() API supports SHA-1, SHA-256, SHA-384, and SHA-512 but not MD5 (which is not considered a cryptographic algorithm by modern standards). The tool uses a pure-JS MD5 implementation for the MD5 row.