What is HMAC? Hash-based message authentication explained

HMAC (hash-based message authentication code) is a short tag computed from a message and a secret key with a hash function such as SHA-256. Anyone who holds the same key can recompute the tag and compare it. A match proves two things: the message was created by someone who knows the key (authenticity), and it hasn't been changed since (integrity). It's the mechanism behind almost every webhook signature.

$ printf '{"event":"ping"}' | openssl dgst -sha256 -hmac 'my-secret'
SHA2-256(stdin)= a4aa04fd005086c11e0953dd0aaa3306e2d8a9fbd9b7d333cc52ce210bdc67f6

How HMAC works

HMAC is defined in RFC 2104. Given a key K, a message m and a hash function H:

HMAC(K, m) = H( (K' ⊕ opad) || H( (K' ⊕ ipad) || m ) )

K' is the key padded to the hash's block size, and ipad and opad are fixed constants. You never implement this by hand. Every language ships it:

// Node.js
crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
# Python
hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
// Go
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
sig := hex.EncodeToString(mac.Sum(nil))

The nested construction is why HMAC is safe even with hashes like SHA-256 that are vulnerable to length-extension attacks, where a naive SHA256(key + message) is not.

HMAC vs a plain hash vs a digital signature

Plain hash (SHA-256)HMACDigital signature (RSA, Ed25519)
Needs a keyNoShared secretPrivate key to sign, public key to verify
Proves integrityOnly against accidentsYesYes
Proves who sent itNoAnyone with the shared keyOnly the private-key holder
SpeedFastFastSlower

Webhooks mostly use HMAC because it's fast, simple and every language supports it. The cost is that the sender and receiver share the same secret. Some specs, including Standard Webhooks, also define an asymmetric option for cases where receivers shouldn't hold a signing key.

How webhooks use HMAC

  1. The provider builds a signed string, such as the raw body, or {timestamp}.{body}.
  2. It computes HMAC-SHA256(secret, signed_string), encodes it as hex or base64, and sends it in a header such as X-Hub-Signature-256 (GitHub), Stripe-Signature (Stripe) or webhook-signature (Standard Webhooks).
  3. The receiver recomputes the HMAC over the bytes it received and compares in constant time.

Including a timestamp in the signed string, and rejecting old timestamps, prevents an attacker from resending a captured request. See webhook replay.

Common mistakes

  • Comparing with ==. Use a constant-time comparison: crypto.timingSafeEqual (Node), hmac.compare_digest (Python), hmac.Equal (Go).
  • HMACing a re-serialized body. The tag covers exact bytes. Parsing and re-stringifying JSON changes them.
  • Hex vs base64. The same HMAC encodes to different strings. Match the provider's encoding.
  • Key encoding. Some providers give the key as base64 (Standard Webhooks whsec_ secrets) and expect you to decode it first. Others use the literal string.
  • Weak algorithms. Avoid HMAC-MD5 and prefer HMAC-SHA256 over HMAC-SHA1 for anything new.
  • Short or guessable keys. Use at least 32 random bytes.

Try it

The HMAC calculator computes and compares HMACs in your browser. To check a full webhook against a provider's format, use the signature verifier. To verify HMAC on incoming webhooks before they're forwarded, see the HMAC authentication docs.

Frequently asked questions

What is HMAC?

HMAC, short for hash-based message authentication code, is a way to compute a short tag from a message and a secret key using a hash function such as SHA-256. Anyone with the same key can recompute the tag and compare. A match proves the message was produced by someone holding the key and was not modified.

What is the difference between HMAC and a hash?

A plain hash like SHA-256 needs no key, so anyone can compute it, and an attacker who changes the message can simply compute a new hash. HMAC mixes in a secret key, so only someone with the key can produce a valid tag.

Is HMAC encryption?

No. HMAC does not hide the message. The message is sent in the clear, or inside HTTPS, and the HMAC tag only proves authenticity and integrity. Use TLS for confidentiality and HMAC for authenticity.

Is HMAC-SHA1 still safe?

HMAC-SHA1 is not broken in the way plain SHA-1 collisions are, but it is deprecated and new systems should use HMAC-SHA256. GitHub, for example, recommends its X-Hub-Signature-256 header over the older SHA-1 X-Hub-Signature header.