HMAC Generator & Verifier
Generate HMAC-SHA256 and HMAC-SHA512 signatures for webhook validation and API request signing. Verify incoming webhook payloads against their expected signatures. All cryptographic operations run in your browser via the Web Crypto API.
Paste an incoming webhook signature to verify it matches the computed HMAC above.
crypto.subtle.sign() API. Your secret key and payload never leave your browser.
HMAC vs a Plain Hash vs a Digital Signature
A plain hash like SHA-256 proves a message hasn't been altered, but proves nothing about who produced it β anyone can compute a matching hash over any content, including tampered content. HMAC closes that gap for symmetric-secret scenarios: it folds a shared secret key into the hashing process, so only someone who holds that secret can produce a tag that verifies correctly, which is exactly the property needed when a receiving server needs to confirm a webhook genuinely came from the platform that claims to have sent it. A public-key digital signature (RSA or ECDSA, as used in TLS certificates and code signing) solves a related but distinct problem: it lets anyone verify authenticity using a public key, without ever needing access to the private signing key β useful when the verifier and signer are different, unrelated parties who've never exchanged a shared secret. HMAC is the simpler, faster, and generally correct choice whenever both sides can safely share one secret in advance, which is true for essentially every webhook-signing scenario; asymmetric signatures earn their extra complexity only when verification needs to happen without ever distributing anything secret at all.
Handling Secrets and Signatures Correctly
- Sign the raw request bytes, never a re-serialized copy: if a webhook handler parses the body into an object and re-serializes it before comparing signatures, differing key order or whitespace between the original payload and the re-serialized version produces a different HMAC and a false-negative failure β capture and sign the exact bytes received, before any JSON parsing.
- Compare signatures with a timing-safe function: a standard string-equality check short-circuits on the first mismatched character, which leaks timing information an attacker can exploit across enough requests to reconstruct a valid signature without ever knowing the secret.
- Store the secret in a secrets manager, not a baked-in environment variable: Vault, AWS Secrets Manager, or a CI/CD platform's encrypted secrets store keep a leaked container image layer from also leaking the webhook secret it was built with.
- Support two active secrets during rotation: verify incoming signatures against either the old or new secret for a transition window, since most webhook-sending platforms have no built-in zero-downtime rotation mechanism of their own.
Signing at Volume
HMAC computation itself is cheap β SHA-256 based HMAC processes payloads fast enough that signature verification is rarely the bottleneck in a webhook receiver, even at high request volume. Where performance actually matters is the surrounding plumbing: verifying a signature correctly requires buffering and hashing the complete raw request body before any JSON parsing begins, which means a receiver handling large payloads or a high sustained request rate needs to budget memory for holding the raw body rather than streaming straight into a parser. Any implementation using a non-timing-safe string comparison for the final checkadds a subtler cost under adversarial load β timing side-channel attacks work by sending enough requests to statistically extract information from tiny response-time differences, so an endpoint sees no functional slowdown from this vulnerability until an attacker starts actively probing it with the volume of requests needed to make the timing signal statistically usable, at which point the fix is procedural (use a constant-time comparison), not about scaling hardware.
When Signatures Don't Match
The most common "valid secret but invalid signature" report traces back to encoding, not the secret itself: a secret copy-pasted from a terminal or a .env file often carries an invisible trailing newline character, and that single extra byte produces a completely different HMAC than the same secret without it. Checking for stray whitespace or newlines around the secret before assuming it's wrong resolves this far more often than re-generating or rotating the secret does. A second frequent cause is exactly the re-serialization problem described above β computing the comparison HMAC over a parsed-then-re-stringified version of the payload rather than the original raw bytes, which silently diverges whenever key order or whitespace differs even slightly from the original. A third, less common cause is an algorithm mismatch β expecting HMAC-SHA256 while the sender actually signed with HMAC-SHA1 or SHA512 β which is easy to rule out quickly by generating the signature with every supported algorithm and checking which one actually matches the value received.
Frequently Asked Questions
What is HMAC?
HMAC (Hash-based Message Authentication Code) is a cryptographic mechanism defined in RFC 2104 that uses a secret key combined with a hash function β typically SHA-256 or SHA-512 β to produce an authentication tag for a message. Unlike a plain hash, which anyone can compute from the data alone, an HMAC requires knowledge of the secret key to produce or verify. This makes HMAC suitable for proving both the integrity of a message (it has not been altered) and its authenticity (it came from someone who holds the same secret). The security of HMAC depends entirely on the secrecy and strength of the key, not on keeping the algorithm secret.
What are HMAC signatures used for?
HMAC signatures are primarily used for webhook payload verification, where a platform signs the HTTP request body with a shared secret so the receiving server can confirm the request is genuine. GitHub sends an X-Hub-Signature-256 header containing sha256=<hex>, Stripe sends a Stripe-Signature header with a timestamp and v1=<hex> components, Slack sends X-Slack-Signature as v0=<hex>, and Shopify sends X-Shopify-Hmac-Sha256 as base64-encoded HMAC. Beyond webhooks, HMAC is used in API authentication schemes, JWT signing (the HS256 and HS512 algorithms in JWTs use HMAC), cookie signing to prevent tampering, and as a key derivation building block in protocols like TLS.
Why should I use timing-safe comparison when verifying HMAC signatures?
Standard string equality operators short-circuit as soon as they find the first non-matching character, which means the comparison takes less time when the strings diverge early. An attacker making thousands of requests with subtly different signatures can measure response times to determine how many leading characters of their forged signature match the expected value β a technique called a timing side-channel attack. Over many requests, this information can be used to reconstruct a valid signature without knowing the secret key. Timing-safe comparison functions like crypto.timingSafeEqual in Node.js, hmac.compare_digest in Python, and hmac.Equal in Go always compare the full length of both strings regardless of where they diverge, eliminating this attack vector. Always use these functions in production webhook verification code.