A webhook endpoint is a public URL, so anyone can post to it. Before you grant an item, mark an order paid or release a creator payout, verify that the message really came from the sender: recompute a keyed hash over the raw body, compare it safely, reject old timestamps, and process each event ID once.
The attack in one paragraph
Suppose your store sends a "purchase completed" webhook and your handler grants the item. If the handler does not verify the sender, an attacker can post a fake completed event for an order they never paid for. The same applies to payout status updates, refund notices and subscription changes.
How signatures work
Most providers sign each webhook with HMAC. RFC 2104 describes HMAC as a mechanism for message authentication using a cryptographic hash function combined with a secret shared key. The sender computes a code over the message with the shared secret. You compute it again with your copy and compare. If they match, the sender knew the secret and the body was not changed.
Exact header names and formats differ by provider, so read the provider's documentation for the details rather than guessing.
Implementation steps
1. Use the raw body. Compute the signature over the exact bytes received, before any JSON parsing or reformatting. Re-serialized JSON often differs from the original.
2. Keep the secret out of code. Store it in a secrets manager and inject it at runtime. OWASP's secrets guidance says not to hardcode secrets, to scope them narrowly and to revoke immediately if exposed.
3. Compare in constant time. Use your language's constant time comparison function so timing differences do not leak information.
4. Check the timestamp. If the provider includes one in the signed data, reject messages outside a short window. This limits replay of captured requests.
5. Make handling idempotent. Providers retry, so the same event can arrive more than once. Record event IDs and ignore repeats, so a retry does not grant a second item.
6. Confirm with the source when stakes are high. For large payouts, fetch the order or payment status from the provider's API using your own credentials rather than trusting the webhook body alone.
7. Respond quickly. Acknowledge fast and do heavy work in a queue, so the sender does not time out and retry.
Rotation and failure modes
Plan for secret rotation by accepting a new and an old secret during a transition. Log verification failures, without logging the secret or the full signature material. A spike in failures can mean an attack or a misconfigured deployment.
Do not disable verification in production to debug. Use a test secret in a test environment.
Checklist
- Raw body signature check on every webhook route.
- Secrets in a manager, with rotation tested.
- Timestamp window and event ID dedupe.
- Provider API confirmation for payouts.
- Alerts on verification failures.
Safeguard can scan repositories for dependencies with known vulnerabilities and enforce pipeline gates, helping keep the code that handles money current. Free scan: https://gold.safeguard.sh/scan.
Quick check
Is it enough to allowlist the sender's IP addresses?
It helps but is weaker than a signature, since address lists change and can be wrong. Verify the signature as the main control.
Sources
Last reviewed: 19 October 2026.