Webhook Security — Signatures, Replay Windows, and the Attack Surface Nobody Audits

A webhook endpoint is a public URL that accepts POSTs and changes state in your systems — which is to say, it's an unauthenticated write API unless you make it otherwise. The security model is well understood and regularly skipped: verify signatures correctly (several popular ways to do it wrong), bound the replay window, and remember that on the provider side, delivering webhooks means making HTTP requests to arbitrary customer URLs — an SSRF machine by design. This post covers both directions of the attack surface.

This post is the API Security series’ missing chapter for the callback direction — where the usual model (you authenticate callers) inverts into you authenticate payloads.

Receiver side: authenticate the payload, not the caller

An unverified endpoint accepts state changes from anyone who learns the URL — and URLs leak (logs, config repos, browser history). Forged events are not exotic: a crafted payment.succeeded against an unverified endpoint is an order-for-free primitive. The defense stack:

Defense in depth rounds it out: TLS always (the signature authenticates, but plaintext still leaks payloads), provider IP allowlists as a layer (brittle alone — ranges change), and the quiet rule that the payload is untrusted input even after verification — authenticity is not well-formedness; it still gets schema validation before touching your systems. And if your webhook consumers feed LLM-based automation, remember the prompt-injection surface: a verified event body can still carry adversarial content.

Provider side: you built an SSRF machine

Delivering webhooks means your infrastructure makes POSTs to URLs customers typed in — the textbook server-side request forgery setup. A malicious (or compromised) customer registers http://169.254.169.254/latest/meta-data/ or http://internal-admin:8080/ and your delivery workers obligingly probe your own network, from inside, with your egress identity. The defenses are specific: resolve-then-validate destination IPs against private/link-local/metadata ranges — and pin the validated IP for the actual connection, because DNS can change between check and connect (the classic rebinding TOCTOU); egress-isolate delivery workers in network segments with no route to internal services, so validation bugs fail safe; bound the response handling — tight timeouts, no redirect-following (or re-validation per hop), capped response reads, and never echoing response bodies back to the customer (that’s a read-SSRF oracle); and rate-limit per destination, because your retry fleet (post 2) aimed at a victim URL is a small DDoS cannon with your reputation on it.

The takeaway: webhook security is payload authentication plus egress hygiene. Receivers verify HMAC over raw bytes, constant-time, scheme-aware, against a rotatable secret list, enforce a signed-timestamp replay window with dedup closing the remainder, treat secrets as production credentials, fail closed loudly — and still schema-validate, because authentic ≠ safe. Providers accept that webhook delivery is SSRF by design and contain it: resolve-validate-pin destinations, egress-isolated workers, bounded non-redirecting responses never echoed back, per-destination rate limits. Both sides audited, the public POST endpoint stops being the soft underbelly it usually is.

Key takeaways

Further reading

Sources & References

the keyed-hash construction webhook signatures use