Choosing a hash: a decision guide
One table mapping the job — integrity, indexing, authentication, password storage, signatures — to a concrete algorithm and parameters, plus the questions that change the answer.
Match the job to the algorithm
| Job | Use | Parameters | Never use |
|---|---|---|---|
| File or payload integrity | SHA-256 (or BLAKE3 for speed) | 256-bit digest | MD5, SHA-1, CRC32 |
| Detect accidental corruption | CRC32 or xxHash | Any width | Any of these for security |
| Hash table key distribution | SipHash or xxHash, seeded | 64-bit, random seed | Unseeded FNV against untrusted input |
| Message authentication | HMAC-SHA256 | 128 to 256-bit tag, per-purpose key | SHA256(key || msg) |
| Sessions and cookies | HMAC-SHA256 or Ed25519 | Include an expiry in the signed payload | A random id with no expiry |
| Password storage | Argon2id | 64 MiB, 3 passes, tuned to about 100 ms | SHA-256, MD5, unsalted anything |
| Password fallback | bcrypt cost 12 or PBKDF2 600k | Per-user salt | A shared salt or no salt |
| Digital signature | Ed25519, or ECDSA P-256, or RSA-PSS | SHA-256 digest | A signature over an unsigned hash |
| Cache key | SHA-256 truncated | 128 bits is plenty | Embedding raw user input as the key |
| Sharding and partitioning | xxHash or SHA-256 truncated | 64 to 128 bits, versioned scheme | Language built-in hash across services |
| Content addressing | SHA-256 | 256-bit digest | SHA-1 for new systems |
| Key derivation | HKDF-SHA256 or BLAKE3 derive_key | Distinct context per purpose | Reusing the raw secret directly |
Four questions that change the answer
- Who can choose the input? If an attacker can, you need collision resistance and, for tables, a seeded hash.
- Who holds the key? A shared secret means HMAC; a private key means a signature; nobody means a plain digest that proves only integrity.
- How long must it stay secure? Signatures and content addresses outlive the code, so choose 256-bit digests and plan for post-quantum migration.
- What is the throughput budget? Password hashing intentionally costs 100 milliseconds; a per-request cache key cannot. Match the cost to the frequency.
# one place for every hash decision, so the choice is reviewable
import hashlib, hmac, secrets
def integrity(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def cache_key(user: str, path: str) -> str:
return hashlib.sha256(f"{user}|{path}".encode()).hexdigest()[:32]
def sign(payload: bytes, key: bytes) -> bytes:
return hmac.new(key, payload, hashlib.sha256).digest()
def new_salt() -> bytes:
return secrets.token_bytes(16)The recurring mistakes
| Mistake | Consequence | Correct approach |
|---|---|---|
| Using MD5 for integrity | Forgery is practical | SHA-256 or BLAKE3 |
Comparing digests with == | Timing leak | A constant-time comparison |
| Reusing one key everywhere | Compromise spreads across systems | Derive a key per purpose with HKDF |
| Truncating a hash to 32 bits | Collisions in ordinary use | Keep at least 64 bits, 128 for uniqueness |
| Hashing a password without a slow KDF | Offline cracking on a GPU | Argon2id with tuned parameters |
| Signing a manifest but not its files | One artifact can be swapped | Sign a hash list covering every artifact |
| Assuming a hash is portable | Keys map differently across languages | Define the algorithm and encoding explicitly |
💡
Write the decision down next to the code. The most expensive hash problems are not cryptographic — they are six teams each choosing differently for the same problem, and nobody able to say later why MD5 was still in the payment path.
FAQ
If I only remember one rule, what should it be?
A hash for integrity is not a hash for authentication. The moment a secret or an attacker is involved, use HMAC, a KDF or a signature instead of a plain digest.
How do I choose between HMAC and a signature?
Use HMAC when both parties share a secret and either may verify. Use a signature when the verifier must not be able to produce valid signatures, as with public downloads or third-party APIs.
Related
Non-cryptographic hashes: CRC32, FNV and xxHash HMAC and keyed hashing
Last refreshed 2026-09-18.