Hashing · 05

HMAC: Hashing With A Key

Hashes prove a message was not modified. They do not prove who wrote it, because anyone can compute a hash. HMAC fixes that: by folding a secret key into the hash, the output becomes unforgeable without the key. The result is a message authentication code that proves the sender held the shared secret.

01

Why Plain Hashes Are Not Enough For Authenticity

A plain hash provides integrity but not authenticity. Alice can hash a message. So can Bob. So can an attacker who intercepts the channel.

If Alice sends "Transfer $100" + SHA-256("Transfer $100") to Bob, Bob can verify integrity: nothing changed in transit. But an attacker could just as easily send "Transfer $10000" + SHA-256("Transfer $10000") and Bob would also verify it successfully. The hash proves nothing about who computed it.

What Bob needs is a function that only Alice could have computed. That requires a secret. A message authentication code (MAC) is a hash-like function with a key. Only someone who knows the key can compute or verify a MAC.

FunctionInputsWhat it proves
Plain hashmessageintegrity (nothing changed)
MACmessage + secret keyintegrity + authenticity (sender knew the key)
Digital signaturemessage + private keyintegrity + authenticity + non-repudiation (third party can verify)
02

Why You Cannot Just Prepend The Key

The naive first attempt: SHA-256(key || message). Looks fine. Only key holders know the result.

It is broken. Hash functions in the SHA-1 / SHA-2 family use the Merkle-Damgard construction, which processes message blocks sequentially and maintains an internal state. Given SHA-256(key || message) and the length of the input, an attacker can extend the hash to SHA-256(key || message || padding || extra) without knowing the key. This is the famous length-extension attack.

It is not a theoretical concern. The Flickr API in 2009 used MD5(secret || message). Attackers exploited length extension to forge authenticated API requests. The same pattern hit several other services in the same era.

Why SHA-3 and BLAKE2 do not have this problem

SHA-3 (sponge construction) and BLAKE2 do not leak internal state in the same way. SHA3-256(key || message) would actually be safe. But HMAC was designed when SHA-1 and SHA-2 were dominant, and it works with any hash function, so it is what protocols actually use.

03

The HMAC Construction

Krawczyk, Bellare, and Canetti published HMAC in 1996, with a formal security proof. The construction nests two hash invocations with two derived keys:

HMAC(K, M) = H((K \u2295 opad) || H((K \u2295 ipad) || M))

Where:

The inner hash uses the key XORed with ipad to scramble it, then hashes the message. The outer hash uses the key XORed with opad to scramble it differently, then hashes the inner output. This nested structure defeats length-extension attacks: appending to the inner hash's output is useless because that output gets re-hashed under a different key.

The names follow the hash used: HMAC-SHA256, HMAC-SHA512, HMAC-SHA3-256, and so on. HMAC-SHA256 is by far the most common.

04

Live: Hash vs HMAC

Same message, two functions: plain SHA-256 and HMAC-SHA256 with a key. Both produce 256-bit outputs. The difference is that anyone can recompute the plain hash, while only key holders can produce or verify the HMAC.

Interactive · Hash vs HMAC

Type a message and a key, see both outputs

The left card shows SHA-256(message): anyone can compute this from the message alone. The right card shows HMAC-SHA256(key, message): only someone with the key can produce or verify this. Try changing only the key and watch the HMAC change while the plain hash stays the same.

Plain SHA-256(message)
computing...
Anyone can compute this. Proves integrity only. An attacker can swap message + hash freely.
HMAC-SHA256(key, message)
computing...
Only key holders can compute or verify this. Proves message authenticity. An attacker without the key cannot forge a valid HMAC for any message.

Try these experiments: Change one character of the message and watch both outputs change. Now change the key and watch only the HMAC change. The plain hash depends only on the message; the HMAC depends on both.

05

Where HMAC Lives

SystemHow HMAC is used
JWT (HS256, HS384, HS512)The token's signature is an HMAC over the header and payload with a server-side key. Used in OIDC, OAuth2, and most stateless auth systems.
AWS Signature Version 4Every AWS API request includes an HMAC-SHA256 over the canonical request, derived from your access key. Same pattern in GCP and Azure.
TLS 1.2 (legacy MAC-then-encrypt)HMAC-SHA256 protected the integrity of each TLS record before AEAD modes took over.
IPsec / IKEPer-packet integrity uses HMAC-SHA1 (legacy), HMAC-SHA256, or HMAC-SHA384.
SSHPer-packet MAC uses HMAC-SHA256 (or newer chacha20-poly1305 AEAD).
HKDF (key derivation)The standard key derivation function in modern protocols is just HMAC applied twice (extract then expand). Used in TLS 1.3, Signal, WireGuard.
Webhooks (GitHub, Stripe, etc.)The provider HMACs each delivery with a shared secret. Your receiver verifies before processing.
06

HMAC vs Digital Signatures

HMAC and signatures both authenticate a message, but they make different trust assumptions.

HMACDigital signature
Key modelShared secret between two partiesPrivate key signs, public key verifies
SpeedVery fast (~3000 MB/s)Slower (10-1000x depending on algorithm)
Output sizeHash size (32 bytes for SHA-256)Larger (64-256+ bytes depending on algorithm)
Anyone can verify?Only key holdersAnyone with the public key
Non-repudiation?No (either party could have produced it)Yes (only the private key holder could have signed)

Pick HMAC when both parties already share a secret and only the two of them need to verify (API auth, internal microservice calls, webhooks). Pick signatures when verification needs to be public, when non-repudiation matters, or when key distribution would not scale otherwise.

07

Modern Alternatives

None of these have displaced HMAC-SHA256 as the de-facto default in protocols, but they appear in newer designs where performance or simplicity is a priority.