Education

What Is HMAC? How It Works and Where It's Used

HMAC (Hash-based Message Authentication Code) is one of the most widely used cryptographic primitives in security engineering. You'll find it in API authentication, webhook verification, JWT signing, and โ€” at the heart of it โ€” in how TOTP 2FA codes are generated. Understanding HMAC helps you build more secure systems and understand why 2FA works.

What Is HMAC?

HMAC is a cryptographic algorithm that takes two inputs โ€” a message and a secret key โ€” and produces a fixed-length output called a MAC (Message Authentication Code). The MAC can be used to verify both that the message hasn't been tampered with (integrity) and that the sender knows the secret key (authenticity).

It was defined in RFC 2104 in 1997 and standardised by NIST. HMAC-SHA256 (using SHA-256 as the underlying hash) is the most widely used variant today.

How HMAC Works

HMAC combines the key and message through the hash function in a specific way to prevent length extension attacks. The formula is: HMAC(K, m) = H((K โŠ• opad) || H((K โŠ• ipad) || m)) where H is the hash function, K is the key, m is the message, and opad/ipad are fixed padding constants.

In practice, you don't implement this yourself โ€” you use a library function like hmac.new(key, message, hashlib.sha256) in Python, or use our browser-based HMAC generator to compute and verify HMACs without any code.

HMAC vs Plain Hash: The Key Difference

A plain SHA-256 hash of a message always produces the same output โ€” anyone can compute it without knowing any secret. This is useful for checksums and fingerprints, but useless for authentication because anyone can fake it.

HMAC requires the secret key. Without the key, you cannot compute the correct MAC for any message. This means the recipient of a message with a valid HMAC knows it came from someone who knows the key โ€” which is the foundation of authentication.

This is why you should never use a plain hash for webhook signature verification or API request signing โ€” always use HMAC.

Where HMAC Is Used in Practice

Webhook verification: Stripe, GitHub, Shopify, and virtually every major platform send webhooks with an HMAC-SHA256 signature in the headers. Your server computes the HMAC of the payload using a shared secret and compares it to the header โ€” if they match, the webhook is authentic.

JWT signing: JWTs with algorithm HS256 use HMAC-SHA256. The header and payload are signed with your secret key. The recipient verifies the signature before trusting any claims in the token.

API request signing: AWS Signature Version 4 uses HMAC-SHA256 to sign API requests. The signature proves the request was made by someone with the AWS secret key.

Session tokens: Some session management systems use HMAC to sign session identifiers, preventing session forgery.

HMAC at the Heart of TOTP 2FA

TOTP (Time-based One-Time Password) is built on HMAC. Specifically, it uses HMAC-SHA1 with the secret key and the current time window as inputs. The result is truncated to produce the 6-digit code you see in your authenticator app. This is why TOTP is described in RFC 6238 as "TOTP: Time-Based One-Time Password Algorithm" and why the underlying primitive is called HOTP (HMAC-based One-Time Password).

The HMAC ensures that only someone who knows the secret key can produce valid codes โ€” and the time component ensures each code is only valid for 30 seconds.

Which HMAC Algorithm Should You Use?

For new systems, use HMAC-SHA256. It produces a 256-bit (32-byte) output, is universally supported, and is considered secure for the foreseeable future. It's the standard for JWTs (HS256), Stripe webhooks, AWS, and most modern APIs.

HMAC-SHA512 is an option for higher security requirements โ€” it produces a 512-bit output but is otherwise equivalent in use. HMAC-SHA1 should be avoided for new systems, though it's still used internally by TOTP for backwards compatibility with authenticator apps.

How to Verify an HMAC Signature, Step by Step

To verify a signed request or webhook, recompute the HMAC of the raw body with your shared secret using the sender's hash algorithm, usually SHA-256. Then compare the result with the signature using a constant-time comparison function, which most languages provide in their standard libraries, to avoid timing attacks. If the payload includes a timestamp, reject signatures older than a few minutes to prevent replay. You can test the whole flow with our HMAC generator without writing any code.

Keeping HMAC Secret Keys Safe

The secret key is the whole security of HMAC: if it leaks, anyone can forge valid signatures. Generate keys with a secure random source, and keep them out of source code and front-end bundles. Use environment variables or a secrets manager, and rotate keys periodically during a transition window. Give each integration its own key so one leak doesn't compromise everything, and revoke a key when a relationship ends.

Frequently Asked Questions

Is HMAC the same as encryption?

No. HMAC doesn't hide the message โ€” it creates a signature that proves the message wasn't altered and came from someone who knows the key. For confidentiality you need encryption, not HMAC.

Can I use a plain hash instead of HMAC for API signatures?

No. A plain hash has no secret, so anyone can recompute it and forge a signature. The key is what makes HMAC verifiable.

Why does TOTP use HMAC-SHA1 instead of SHA-256?

TOTP was designed when SHA-1 was the standard choice, and switching would break compatibility with existing apps. It is still safe here because the key stays secret.

What happens if the signature doesn't match?

Treat the message as unauthenticated: reject it, log the failure, and do not process it. The sender likely doesn't hold the key.

HMAC vs Digital Signatures: Which One Do You Need?

HMAC is symmetric: the same secret key signs and verifies, so sender and receiver must share and protect one key. Digital signatures are asymmetric โ€” a private key signs and a public key verifies โ€” which lets many parties check a message without holding any secret. Both prove that a message is authentic and unmodified; the difference is who can verify it and what a leaked key destroys.

Use HMAC when you control both ends of the conversation: your server talking to a payment provider, a webhook sender and your endpoint, or two services inside your own infrastructure. Use digital signatures when a message must be verifiable by people or systems that cannot be trusted with a shared secret. One practical rule: if you can keep the key out of the verifier's hands, asymmetric is the safer choice; if both sides must hold a key anyway, HMAC is simpler and faster.

Five HMAC Mistakes That Break Security

Comparing signatures with a normal string comparison is the classic error โ€” it opens a timing attack that lets an attacker guess the signature byte by byte; use the constant-time compare function from your language's standard library instead. Hashing the wrong bytes is second: the signature must be computed over the exact raw body received, so do not reformat, re-encode, or pretty-print the payload before signing, and be aware that some libraries add or strip whitespace.

Three more mistakes show up constantly: mixing up the hash algorithm on either side (a SHA-256 sender against a SHA-512 verifier fails silently), leaking the secret into client-side code or logs where any XSS or log viewer can read it, and skipping the timestamp check, which allows an attacker to replay a captured request. When in doubt, implement the verification exactly as the provider's documentation describes, then test with a real payload before going live.

A Concrete Example: Verifying a Stripe-Style Webhook

Imagine a payment platform delivers an event to your endpoint with a header like Stripe-Signature: t=1700000000,v1=9c8f.... Your server takes the raw request body, extracts the timestamp t and the expected signature v1, builds the signed string as t + "." + body, and computes HMAC-SHA256 of that string with the webhook secret. If the computed value matches the v1 value and the timestamp is recent, the webhook is authentic.

Two details decide whether this works in production: compare the signatures with a constant-time function, and reject any event whose timestamp is older than a few minutes even if the signature matches. Every major language ships an HMAC implementation, so the whole check is a handful of lines โ€” and you can practice the flow with the 2faco HMAC generator by entering the body, secret, and algorithm and checking the output yourself.

Choosing and Managing HMAC Secret Keys

Key strength is the whole game: a 256-bit random key is the baseline for HMAC-SHA256, generated from a secure random source rather than a phrase, date, or dictionary word. You can store the key as raw bytes, hex, or base64, but whatever the encoding, the key itself must never appear in source code, front-end bundles, or commit history โ€” environment variables or a secrets manager is the right home.

Give each integration its own key so one leaked secret does not unlock everything, and rotate keys on a schedule or whenever a team member leaves. Plan a short transition window where both old and new keys are accepted, and revoke the old key once every consumer has switched. Write the key-rotation procedure down before you need it โ€” the teams that improvise rotation under pressure are the ones that accidentally revoke production signing mid-outage.

Related Articles