All articles
Integrity

HMAC Explained: Keyed Hashing for Message Integrity

Generate Secret Keys team June 4, 2026 7 min read

HMAC (Hash-based Message Authentication Code) answers a deceptively simple question: "Did this message really come from someone who knows the shared secret, and has it been tampered with?" It's the quiet workhorse behind API request signing, webhook verification, and JWT tokens.

Hash vs HMAC

A plain hash like SHA-256 proves integrity — that data hasn't changed — but not authenticity, because anyone can compute a hash. If an attacker alters a message, they can simply recompute the hash to match. HMAC fixes this by mixing a secret key into the hashing process. Without the key, an attacker can't produce a valid code, so a matching HMAC proves both that the message is intact and that it came from someone holding the secret.

How HMAC works

Conceptually, HMAC hashes the message together with the secret key in a specific two-pass construction (an inner and outer hash with padded key material). You don't implement this by hand — every crypto library exposes it directly. The inputs and output are:

  • Inputs: a secret key and a message.
  • Output: a fixed-size code, e.g. 32 bytes for HMAC-SHA-256.

The sender computes the code and sends it alongside the message. The receiver, who also knows the key, recomputes the code over the received message and checks that the two match.

Where HMAC is used

  • API request signing. Cloud providers sign requests with HMAC so the server can verify the caller without sending the secret over the wire.
  • Webhook verification. Services like Stripe and GitHub send an HMAC signature header so you can confirm a webhook genuinely came from them.
  • JWT (HS256/384/512). The signature on an HMAC-signed JWT is an HMAC over the header and payload.
  • Cookie and token integrity. Detecting tampering with signed session data.

Choosing the hash and key

Pair HMAC with a modern hash — SHA-256, SHA-384, or SHA-512. HMAC-SHA-256 is the common default. The key should be random and at least as long as the hash output (32 bytes for SHA-256). A short, guessable key undermines the whole scheme, exactly as it does for JWT secrets.

Verify safely: constant-time comparison

When checking an incoming code, never compare it with an ordinary == string comparison. Normal comparisons return as soon as they find a mismatched character, and that tiny timing difference can leak the correct value byte by byte — a timing attack. Use a constant-time comparison function (crypto.timingSafeEqual in Node, hmac.compare_digest in Python) that always takes the same amount of time.

Generate an HMAC now

The Crypto Tools tab includes an HMAC generator: paste a secret and a message, choose SHA-256/384/512, and get the signature in hex, Base64, or Base64URL — computed locally with the Web Crypto API.

Try signing a message. Generate an HMAC-SHA-256 signature in your browser to see exactly what a verifier checks against.

Open the HMAC tool