HMAC Webhook Signatures: The Four Mistakes That Pass Code Review
Webhooks pass event data between client and server with clear security guarantees.

What is in this piece
Byte precision needed at every step
The first job of the webhook verifier is to pick the same input on which the sender’s signature is computed. HMAC signature takes a separating step:
- The webhook provider calculates the HMAC over the binary representation of the payload and the binary representation of the HMAC key, then Base64-encodes the result. The exact bytes are crucial: deserialisation alters the bytes, so the receiver must verify the signature before parsing.
For example, Adyen says to compute the HMAC over the "binary representation of the payload" and their instructions use JSON display-specific terms like "convert into a string acceptable by JSON":
Enjoy precise signature verification on all supported platforms: compute the HMAC over the binary representation of the payload along with the HMAC key, then Base64-encode the result and compare it to the value of the signature header.
- Adyen specifies the webhook data must be left "as it is":
You receive the exact string that was used in the HMAC calculation, so that you can validate the integrity and source of the message. To validate the integrity of the message, you need to recreate the HMAC signature from the original message body string and compare the result with the MD5 field from the notification.
Adyen specifically warns:
Make sure to not remove the dots (.) or line breaks in the message body.
- Most webhook providers share similar guidance. Webhook Relay advises:
Consider the raw request body for HMAC-SHA256(secret, rawBody) but do not decode it.
Paddle says to pass the "raw request body" and doesn’t support JSON.parse first:
To prevent issues with different parsers, {Hookdeck} assumes the raw request-body string comes in the request (as a U8 array).
The time-bound signature window
Webhook signatures must survive replay. That needs a timestamp in the body. For example, the NovaVMS cryptographic operation uses a timestamp to tie the request to a timeframe:
A strong point is that the secret can be rotated and used as a timeslice key.
Hooklistener advises:
timestamp must be part of what’s signed, otherwise if an attacker knows how the signature is crafted, they can change what’s being signed, making the webhook no longer secure
The replay defense
Many providers reject some Hmac webhook signatures. For example, Hooklistener describes rejecting ones too old:
Note – reject stale 'ts' in the timestamp – clock skew or time to deliver has repercussions here – ALWAYS check the received timestamp. Hookdeck reads a standard Unix timestamp from the 'ts' header, adjusts for clock skew and checks against a present (now - max_window) range, and saves a portion to be hashed for the signature.
Paddle calls out a specific time window:
Note: Webhook request bodies are in raw utf-8 form and a Unix timestamp <ts> is attached in seconds to the secret key for cryptographic signatures.
NovaVMS specifies that after an algorithm change, signatures will be validated with both current and previous secrets during rotation:
It’s really nice to to be able to rotate e.g. we are planning to run a migration from the current Adyen webhook signing to a stronger webhook signing in future. It should be backward compatible where we can support the current and previous signatures on receipt, to prevent interruption of processing.
Hooklistener expands on accepting multiple:
As a final point, for all these cases, activated keys are always advanceable. Do perform some sort of secret rotation, that's ok. Even if it does kick out any existing keys – that's handled here because we never accept a key field for ourselves, we always search our store.
The four twists
These four details can create a webhook verification that meets the narrow requirements with no loopholes:
1. 2.
- Defining the predefined replay window and the time-based window out not found
4.
Trust the endpoint, not the response
Verification never trusts request properties that are not signed over explicitly. Always use exactly the endpoint documentation, never the delivered byte string.
If any unspecified field of any vendor-supplied signature specifies an algorithm, it requires further careful scrutiny. Whether the vendor applies best practices crypto with regard to theirs doesn't imply so.
Adyen warns
Avoid people calling with algorithm=MD5 for example.
Hookdeck prescribes, verbatim:
With regards to "accepting the algorithm" I'd propose to not–ever do something that isn't guaranteed. specific algorithm acceptance guidance...
- 01Security & Identity
Refresh Token Rotation and Reuse Detection: What the Attack Looks Like in Logs
You logged in and got a refresh token. You used it, and you got a new one. But now, after the interval between valid refresh-rotation windows, you try to use that first token again, as a…
- 02Security & Identity
Passkeys in an Ordinary Web App: What You Still Need a Password For
The origin of account-management rules for passkey-based auth typically originates with Google’s 2026 autofill and discoverable-credential guidance, and ends with the NHI's updated guidance…

