What Is an HMAC? Keyed Hashing Explained
You've integrated a webhook before β Stripe, GitHub, some payment provider β and buried in the docs was a slightly scary instruction: "verify the signature." That signature was an HMAC, and if you skipped the step (I did once, on a side project I'd rather not talk about), you left the door unlocked. So what is an HMAC, really, and why does every serious API lean on one?
Here's what you'll be able to do by the end: explain in one sentence what an HMAC proves, tell it apart from a plain hash, and know exactly why the receiver of a webhook can trust it came from who it claims. No cryptography degree required β if you can picture sealing an envelope, you're most of the way there.
A plain hash proves the message, not the messenger
Start with what an ordinary hash gives you. Run a message through the SHA256 Generator and you get a fixed fingerprint; change a single character and the fingerprint changes completely. That's wonderful for spotting tampering β but there's a gap. Anyone can compute it.
Think about what that means for a webhook. If the only protection is "here's the message and its SHA-256 hash," an attacker who forges a fake message can just as easily compute a matching hash and send both. The hash proves the message wasn't garbled in transit. It says nothing about who sent it. It's the identity of the data, not the identity of the sender.
The wax seal: what a shared secret adds
Here's the picture I keep coming back to. Imagine a letter closed with a blob of wax, and pressed into that wax, the sender's personal signet ring. Anyone can read the letter, and anyone can see that it's sealed β but only the person holding that exact ring could have made that impression. Break the seal to tamper with the letter, and you can't remake it without the ring.
An HMAC is that seal, and the shared secret key is the signet ring. The message travels in the open β the HMAC doesn't hide it β but the seal can only be produced by someone holding the key, and it can only be verified by someone holding the same key. That's the whole idea: an HMAC proves two things at once. The message is unchanged (integrity), and it came from a key-holder (authenticity). A plain hash only ever promised the first.
If that clicked, the rest is downhill.
So it's just hash(secret + message)? Not quite β and the reason matters
Your first instinct β mine too β is that you'd build this by gluing the secret onto the front of the message and hashing the lot: SHA256(secret + message). Reasonable guess. It's also subtly broken, and the flaw is worth understanding, because it's why HMAC has the shape it does.
Most classic hashes (MD5, SHA-1, SHA-256) use a design called MerkleβDamgΓ₯rd, which chews through a message in blocks while keeping a running internal state β and the final digest is that state. So an attacker who sees SHA256(secret + message) can, without ever learning the secret, pick the computation back up and produce a valid hash for secret + message + anything_they_append. That's a length-extension attack, and it quietly breaks the naive scheme.
HMAC's designers closed the hole by hashing twice, with the key folded in both times. In shorthand: H((key β opad) + H((key β ipad) + message)), where ipad and opad are two fixed padding constants. Don't memorize that β the point is that the inner hash gets hashed again with the key, so there's no exposed internal state left to extend. The nesting is the security. Generate one for real with the HMAC Generator and you'll notice it asks for a key and an algorithm the plain SHA512 Generator never does.
Watching an HMAC do its job: signing a webhook
Let's walk the actual flow, because this is where everything lands:
- You and the API provider agree on a secret key ahead of time β usually shown once in a dashboard. Both sides store it, and it's never sent over the wire again.
- When an event fires, the provider builds the payload (say
{"event":"payment.succeeded"}), computesHMAC-SHA256(payload, secret), and sends the payload with that HMAC in a header. - Your server receives the request, takes the raw body, and computes its own
HMAC-SHA256(body, secret)using the copy of the key it already holds. - You compare the two HMACs. Match? The message is genuine and untouched. Mismatch? Someone tampered with it, or it didn't come from the key-holder β so you reject it.
That's HMAC-SHA256, the variant you'll meet most often: an HMAC built on SHA-256. An attacker can copy the payload all day, but without the secret they can't press a seal your server will accept.
Two things people get wrong
First, and it's a sneaky one: compare HMACs in constant time, not with a normal ==. A naive string comparison quits at the first mismatched character, and that tiny timing difference can, over many attempts, leak the correct signature one byte at a time. Most languages ship a timingSafeEqual-style function for exactly this. Use it.
Second: an HMAC is not encryption. This trips up the same folks who assume Base64 is encryption β an HMAC isn't hiding your message, it's vouching for it, and the payload rides along in plain sight. If the contents need to be secret too, that's a job for TLS or real encryption, layered on top.
One more boundary, since hashes tend to blur together: HMAC is for messages, not passwords. Storing a password calls for a deliberately slow hash like bcrypt instead β our MD5 vs SHA-256 vs bcrypt guide draws that line, and you can feel the slowness yourself in the Bcrypt Generator. Different seal, different door.
The one-line version
An HMAC is a hash with a shared secret pressed into it β a wax seal for data. It proves a message arrived unchanged and came from someone holding the key, which is exactly what a naked hash can't do. Next time an API tells you to "verify the signature," you'll know you're just re-pressing the seal with your own copy of the ring β and you can try it right now in the HMAC Generator, then confirm two digests match with the Hash Compare tool.
Try the tools
Frequently Asked Questions
What is an HMAC in simple terms?
An HMAC is a hash with a shared secret mixed in. Like a plain hash it produces a fixed fingerprint of a message, but because the secret key is part of the calculation, only someone who holds that key can produce or verify the fingerprint β which proves the message is both unchanged and from a trusted sender.
What's the difference between HMAC and a hash like SHA-256?
A plain SHA-256 hash proves a message wasn't altered, but anyone can compute it, so it says nothing about who sent the message. HMAC adds a secret key, so a valid HMAC also proves the sender holds that key. HMAC uses a hash like SHA-256 internally β it adds authenticity on top of integrity.
Is HMAC encryption?
No. An HMAC doesn't hide the message β the data travels in plain sight alongside the signature. HMAC only proves the message is unchanged and came from a key-holder. If you also need the contents kept secret, use encryption such as TLS on top of it.
What is HMAC-SHA256?
HMAC-SHA256 is an HMAC that uses SHA-256 as its internal hash function. It's the most common variant for signing API requests and webhooks. You can also build HMACs on SHA-1 (legacy) or SHA-512 when a larger digest is required.
Where is HMAC actually used?
Signing webhooks (Stripe, GitHub, and most providers), authenticating API requests, JWTs signed with the HS256 algorithm, and any situation where a receiver needs to confirm a message came from a specific sender and wasn't tampered with in transit.
Desmond Okafor writes for CodeUtilityKit, where the team builds free, privacy-first developer tools that run entirely in your browser. Every guide is written and reviewed by developers who use these tools daily.