← Glossary

Cryptography & Hashing

HMAC (Hash-based Message Authentication Code)

Also: keyed hash · message authentication code · HMAC-SHA256 · webhook signature

Last reviewed:

In one sentence

An HMAC is a hash computed with a shared secret key mixed in, so it proves two things at once — the message was not altered, and it came from someone who holds the key — which is why webhooks and APIs sign their payloads with it.

Why it matters

A plain hash tells you a message was not accidentally corrupted — but anyone can recompute it, so it proves nothing about who sent the message. HMAC adds a shared secret to the hash. Now only the two parties who know the key can produce a valid code, so a matching HMAC means both "unchanged" and "from someone with the key". That combination — integrity plus authenticity — is what makes it the workhorse of webhooks, API request signing and token verification.

When a service like Stripe or GitHub sends your endpoint a webhook, it signs the raw body with a secret you both hold and puts the HMAC in a header. You recompute it and compare; if they match, you know the payload is genuine and untouched. Without that check, anyone who guesses your URL can forge events.

How it is built

HMAC is a specific, careful construction — not just "hash the key and the message together" (which is vulnerable to length-extension attacks). RFC 2104 defines it as two nested hashes with the key XORed into distinct inner and outer pads: H((K ⊕ opad) || H((K ⊕ ipad) || message)). You do not implement this by hand; every crypto library provides it.

  • Name it by its hash: HMAC-SHA256 is HMAC over SHA-256. Prefer SHA-256 or better; HMAC-SHA1 is legacy and should not be used for new designs.
  • Output is the hash's width (32 bytes for SHA-256), rendered as hex or Base64 depending on the API.
  • It is symmetric: the same secret computes and verifies. That is the crucial difference from a digital signature, where a private key signs and a public key verifies — HMAC gives no non-repudiation, because either party could have produced the code.

One implementation rule matters enormously: verify in constant time. Comparing the received and computed codes byte-by-byte with an early exit leaks timing that can be used to forge a code; use a constant-time comparison.

Try it yourself hands-on

Reproduce a webhook signature check the way a receiving server does it.

  1. Open the HMAC Generator. Put the webhook's raw JSON body in Message, your webhook Secret key in the secret field, choose SHA-256 (recommended), and set the output encoding (hex or Base64) to match what the sender uses. The computed HMAC is the value you compare against the sender's signature header.
  2. Paste the sender's signature into Expected HMAC and watch the tool report ✓ Match or ✗ Mismatch. Change one character of the message body and it flips to Mismatch instantly — that is tamper detection in action.
  3. Switch the algorithm to SHA-1 (legacy) and note the different, shorter output: this is why sender and receiver must agree on the exact hash. A mismatch is very often just SHA-1-vs-SHA-256 or hex-vs-Base64, not an attack.
  4. See HMAC in a real protocol: the JWT Decoder uses HMAC-SHA256 to verify an HS256 token — the same primitive, signing header.payload instead of a webhook body.

Result: you can validate any HMAC-signed payload and diagnose the usual "signatures don't match" causes (wrong hash, wrong encoding, whitespace in the body) rather than disabling the check.

Common misreadings

  • HMAC is not a signature. Both sides share the secret, so it cannot prove which party sent a message to a third party. When you need non-repudiation, use asymmetric signatures.
  • Sign the exact bytes, not a re-serialised object. Reformatting the JSON before hashing changes the bytes and breaks verification — hash the raw received body.
  • Compare in constant time. A naive == on the codes can leak timing; use the library's constant-time equality (as the tool does) to avoid a byte-at-a-time forgery.