Cryptographic Hashing — Reference.
Practitioner-level hashing reference: when collision resistance matters, when length-extension bites, and what to pick today.
Use-case → choice
- Password storage. Argon2id (preferred), bcrypt (acceptable), scrypt (acceptable). Never SHA-256/SHA-512 directly, never MD5. OWASP-recommended Argon2id params (2024): t=2, m=19 MiB, p=1 for interactive; t=3, m=64 MiB, p=4 for higher-stakes. Tune until a single hash takes ~100 ms on your hardware.
- Generic content addressing. SHA-256. Fast enough, ubiquitous library support, no known practical break. BLAKE3 if performance matters more than ecosystem.
- File-equality / dedup where attacker cannot supply files. BLAKE3 or xxHash. Massive throughput; collision resistance not required.
- Authenticated message integrity. HMAC-SHA-256 if you must use a hash. Better: AEAD (AES-GCM, ChaCha20-Poly1305) which gives encryption + integrity together. Don't hand-roll MAC by concatenating key and message.
- Digital signatures. SHA-256 with RSA-PSS or ECDSA P-256 or Ed25519. Avoid SHA-1, MD5 (no longer collision-resistant — chosen-prefix collisions demonstrated).
- Key derivation from low-entropy input. HKDF (extract + expand) using SHA-256. Not bcrypt/Argon2 (those are for password verification, not key derivation).
- Commitment schemes / Merkle trees. SHA-256 (Bitcoin) or BLAKE3 / Poseidon (zk-friendly).
- Random ID generation. Don't hash, just use CSPRNG bytes encoded base32. 16 bytes = 128 bits = safe.
Length-extension attack
- What it is. Given
H(key || message)and length ofkey, attacker computesH(key || message || padding || extra)without knowingkey. - Vulnerable. SHA-1, SHA-256, SHA-512 (any Merkle-Damgård construction).
- Not vulnerable. SHA-3 (Keccak sponge), BLAKE2, BLAKE3, HMAC over any hash.
- Fix. Use HMAC:
HMAC(key, message). Library will do it correctly. Or use AEAD.
Common mistakes
- MD5 for "non-security" uses. Then someone uses the result for cache key, then for ETag, then for security boundary. Use BLAKE3 or SHA-256 instead — same convenience, no future foot-gun.
- SHA-256 for password storage. GPU cracking: 10 GH/s. Argon2id: ~10 H/s/GB. 9 orders of magnitude difference.
- Truncating a hash to "save space". 64-bit truncated SHA-256 has 32-bit collision resistance via birthday bound — practical to attack.
- Salt reuse / no salt. Reused salt = rainbow-table attack. Per-user random salt always.
- Comparison via
==on hashes used for auth. Timing leak. Use constant-time compare (crypto.timingSafeEqual,hmac.compare_digest). - Custom MAC construction. "H(key || msg)" — vulnerable to length extension. "H(msg || key)" — collision on H lets you forge. Just use HMAC.
Migration paths
- MD5 password hashes →. On next login, rehash with Argon2id and store. Old MD5 hash deletable after grace window where users have logged in.
- SHA-1 signatures →. Re-sign with SHA-256. Verify both during transition; reject SHA-1 after cut-off.
- HMAC-SHA-1 (e.g., legacy AWS Sig v2) →. HMAC-SHA-256 (Sig v4).
- bcrypt approaching cost-factor ceiling →. Argon2id. Wrap during user login.
Rule of thumbFor 99% of use-cases the decision tree is: passwords → Argon2id; integrity with key → HMAC-SHA-256 or AEAD; integrity without key → SHA-256 or BLAKE3; random IDs → CSPRNG, not a hash. Almost everything else is a custom mistake.
Related notes in this domain
From reference to evidence