HMAC (Hash-based Message Authentication Code)¶
A keyed hash that proves a message came from someone who knows the secret and wasn't changed on the way.
What it is¶
A plain hash like SHA-256 tells you whether data changed, but anyone can compute it, so it proves nothing about who sent the data. HMAC mixes a shared secret key into the hash. Only someone with the key can produce a matching tag, so a correct tag means two things at once: the message is intact, and it came from a holder of the key.
It is a MAC (message authentication code), not encryption — it does not hide the message, it vouches for it.
How it works¶
HMAC wraps an ordinary hash H with the key on both ends:
HMAC(K, m) = H( (K ⊕ opad) || H( (K ⊕ ipad) || m ) )
ipadis the byte0x36repeated to the hash's block size,opadis0x5c.- The key is hashed first if it's longer than a block, or zero-padded if shorter.
The nested structure is what makes HMAC safe against length-extension attacks,
which is why you use HMAC instead of just hashing K || m yourself.
When to use it¶
- Verifying a message between parties that share a secret — API request
signing, webhook signatures, session cookies, JWT (the
HS256algorithm). - As a building block — HKDF key derivation and PBKDF2 are both built on HMAC. See HKDF and PBKDF2.
- Not for passwords. A password is low-entropy; use a slow password hash instead.
- Not when the two sides don't share a secret. Use a signature (Ed25519, ECDSA) so a public key can verify.
Example (Python)¶
import hmac, hashlib
key = b"shared secret"
message = b"transfer $100 to Bob"
tag = hmac.new(key, message, hashlib.sha256).digest()
# The receiver recomputes the tag and compares in constant time.
ok = hmac.compare_digest(tag, hmac.new(key, message, hashlib.sha256).digest())
Always compare tags with compare_digest, not ==. A normal compare returns
early on the first wrong byte, and that timing difference can leak the right tag.
Security notes¶
- HMAC is only as secret as the key — store and share it like any other secret.
- Strength tracks the hash: HMAC-SHA256 gives ~256-bit security and is the safe default. HMAC-SHA1 is still unbroken (the SHA-1 collision attacks don't apply to HMAC), but new systems should use SHA-256.
- HMAC proves authenticity, not freshness — add a timestamp or nonce if you need to stop replay of an old, validly-tagged message.
See also¶
- SHA-2 — the hash HMAC is built on
- HKDF / PBKDF2 — both built on HMAC
- Block-cipher MACs / Universal hash MACs — CMAC, GMAC, Poly1305 and UMAC, the other MAC families
- KMAC / SipHash — SHA-3's own MAC, which needs no HMAC wrapper, and a keyed hash for hash tables
- Cryptography FAQ — encrypt vs. sign vs. hash
References¶
- RFC 2104: HMAC: Keyed-Hashing for Message Authentication
- FIPS 198-1: The Keyed-Hash Message Authentication Code (HMAC)