HMAC Explained: Hash-Based Message Authentication Codes
HMAC (Hash-based Message Authentication Code) is a widely used construction that proves a message is both authentic (from someone holding the secret key) and intact (not modified in transit). It appears throughout modern security: TLS record authentication, API request signing, JSON Web Tokens, cookie integrity, and key derivation all rely on HMAC. Understanding how HMAC works and why its specific structure matters is essential for building systems that resist tampering and forgery.
What Problem HMAC Solves
A cryptographic hash like SHA-256 detects accidental changes, but on its own it does nothing to stop a deliberate attacker. Anyone can recompute a hash after modifying data. What we need is a message authentication code (MAC): a tag computed with a secret key so that only parties holding the key can produce or verify it.
A MAC provides two guarantees simultaneously:
- Integrity: any change to the message invalidates the tag.
- Authenticity: a valid tag proves the sender knew the shared secret key.
HMAC is the standard way to build a MAC out of an existing cryptographic hash function such as SHA-256 or SHA-512, and it is standardized in RFC 2104 and FIPS 198.
The Naive Approach and Why It Fails
An obvious but flawed idea is to simply hash the key concatenated with the message as H(key || message). This is insecure with Merkle-Damgard hashes like SHA-256 because of the length-extension attack. Since these hashes output their full internal state, an attacker who sees a valid tag can append data and compute a valid tag for the extended message without knowing the key.
Prepending is not the only broken variant, and appending the key has its own weaknesses. HMAC's nested structure was specifically designed to avoid all of these pitfalls with a construction that has a formal security proof.
How HMAC Is Constructed
HMAC applies the underlying hash function twice, with the key mixed in through two different padding constants. The definition is:
HMAC(K, m) = H( (K' XOR opad) || H( (K' XOR ipad) || m ) )
Where:
- H is the hash function (for example SHA-256).
- K' is the key, adjusted to the hash's block size: keys longer than the block are hashed first, and shorter keys are zero-padded to the block length.
- ipad is the byte 0x36 repeated to fill one block (the inner pad).
- opad is the byte 0x5C repeated to fill one block (the outer pad).
The mechanics proceed in two passes:
- Inner hash: XOR the key with ipad, prepend it to the message, and hash the result.
- Outer hash: XOR the key with opad, prepend it to the inner hash output, and hash again.
The nested outer hash is what defeats length-extension attacks: because the attacker never sees the raw inner state (it is wrapped inside a second hash keyed with opad), they cannot extend it. When HMAC uses SHA-256 it is called HMAC-SHA-256, and its security tracks the strength of the underlying hash.
Verifying an HMAC Safely
Verification recomputes the tag over the received message and key, then compares it to the received tag. One subtle but important detail is constant-time comparison. Comparing tags with an ordinary byte-by-byte check that returns early on the first mismatch leaks timing information, which can let an attacker recover a valid tag byte by byte. Secure implementations recompute the expected tag as expected = HMAC(key, message) and compare it against the received tag with a constant-time equality function that always inspects every byte of both values before returning a result. Because the comparison time does not depend on how many leading bytes matched, no timing signal is exposed to the attacker.
Security Properties and Key Management
HMAC's security rests on the secrecy of the key and the properties of the hash function:
- Key length: a key at least as long as the hash output (for example 256 bits for HMAC-SHA-256) is recommended. Very short keys reduce security to the key's entropy.
- Resistance to hash weaknesses: HMAC is remarkably robust. Even HMAC-SHA-1 has resisted practical forgery despite SHA-1's collision weaknesses, because HMAC does not require collision resistance in the same way signatures do. New systems should still prefer SHA-256 or stronger.
- Existential unforgeability: without the key, an attacker cannot produce a valid tag for any new message, even after observing many valid message-tag pairs.
Keys should be generated from a strong random source, stored securely, and rotated according to policy.
Where HMAC Is Used
HMAC is a versatile primitive that appears in many protocols and patterns:
- API authentication: signing requests so a server can verify the caller holds a shared secret and the request was not altered.
- JSON Web Tokens (JWT): the HS256 algorithm is HMAC-SHA-256 protecting token integrity.
- TLS and IPsec: authenticating records and packets, though modern AEAD ciphers increasingly handle this internally.
- Key derivation: HKDF, a widely used key-derivation function, is built on HMAC to expand and extract keys.
- Cookie and token integrity: detecting tampering with signed values stored client-side.
HMAC provides authentication but not confidentiality. When you need both, combine it with encryption using an encrypt-then-MAC scheme, or better, use a purpose-built authenticated encryption mode that integrates the MAC for you.
Key Takeaways
- HMAC turns a cryptographic hash into a keyed message authentication code, providing both integrity and authenticity.
- Its nested two-pass structure with the ipad (0x36) and opad (0x5C) constants defeats the length-extension attacks that break naive keyed hashing.
- HMAC-SHA-256 is a common, well-trusted choice, and HMAC remains secure even when the underlying hash has some weaknesses.
- Always verify tags in constant time and use keys with sufficient entropy, ideally at least the hash output length.
- HMAC provides authentication only; pair it with encryption or use an AEAD mode when confidentiality is also required.