Engineer's toolkit
Hash functions
A hash turns any input into a fixed-size fingerprint. A secure hash is hard to reverse and hard to collide, and MD5 and SHA-1 no longer meet that bar.
A hash function takes an input of any size and returns a fixed-size digest. The same input always gives the same digest. SHA-256 always returns 256 bits, written as 64 hex characters, whether the input is one byte or a 4 GB file.
The useful part is what happens when the input changes. A one-character edit gives a digest that looks unrelated to the old one.
Walking through it
Here are two inputs that differ by one period:
SHA-256("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-256("hello.") = 1589999b0ca6ef8814283026a9f166d51c70a910671c3d44049755f07f2eb910Nothing in the second digest hints at the first. This is the avalanche effect. A secure hash spreads every input change across the whole output.
A secure hash has three properties:
- Preimage resistance: given a digest, you can't find an input that produces it.
- Second-preimage resistance: given one input, you can't find a different input with the same digest.
- Collision resistance: you can't find any two inputs with the same digest.
Collision resistance is the weakest of the three because of the birthday paradox. For an n-bit hash, finding a specific preimage takes about 2^n tries. Finding some collision takes only about 2^(n/2). SHA-256 therefore gives about 128 bits of collision resistance, which is still far out of reach.
You meet hashes everywhere: Git commit IDs, Docker image digests, download checksums, Merkle trees in databases, and as the building block inside HMAC and signatures.
Never use MD5 or SHA-1 for security
MD5 (128 bits) has had practical collisions since 2004. In 2012, the Flame malware used an MD5 collision to forge a certificate that Windows Update trusted. SHA-1 (160 bits) fell in 2017, when Google and CWI Amsterdam published SHAttered: two different PDFs with the same SHA-1. In 2020, the SHA-1 is a Shambles team produced a chosen-prefix collision. That is worse, because the attacker picks both meaningful prefixes.
MD5 is fine as a checksum where nobody gains from a collision, such as a cache key or an ETag. If an attacker might want two inputs to collide, use SHA-256, SHA-3 or BLAKE2 and BLAKE3.
Warning
sha256(secret + message) is not a safe way to authenticate a message. SHA-256 is vulnerable to length extension. Knowing sha256(secret + msg) and the secret's length, an attacker can compute a valid hash for msg plus extra data, without knowing the secret. Flickr's API signatures were broken this way in 2009. Use HMAC instead, covered in the next page.
Common mistakes
- Using MD5 or SHA-1 anywhere an attacker could benefit from a collision.
- Hashing a secret together with a message to prove authenticity.
- Using a fast hash to store passwords. Passwords need a slow hash, covered in password hashing.
Check yourself
- Why does
"hello."produce a digest unrelated to the digest of"hello"? Answer: the avalanche effect. A secure hash spreads any input change across all output bits. - Why isn't
sha256(secret + message)a safe MAC? Answer: length extension lets an attacker extend the message without knowing the secret. HMAC prevents this.
Key takeaways
- A hash maps any input to a fixed-size digest, and the same input always gives the same digest.
- A secure hash resists preimages and collisions. Collision resistance is the weakest, because of the birthday bound.
- MD5 and SHA-1 are broken for anything adversarial. Use SHA-256, SHA-3 or BLAKE3.
- Don't build authentication from a bare hash. Use HMAC.
Further learning
Next, see how a hash combined with a secret key becomes a message tag in HMAC and webhook signing.
Related
Cryptography for engineers
The few building blocks you need for everyday security decisions, which question each one answers, and where teams go wrong. Start here for the map.
HMAC and webhook signing
A keyed hash proves a message came from a key holder and was not changed. It protects webhooks, signed URLs and TOTP codes.
Secure randomness and tokens
Where keys, nonces, salts and session tokens must come from, why Math.random is unsafe, and how many bits a token needs.