Webhook Signature Verification for AI Agents
Stripe, Slack, GitHub and OWASP agree on four checks for an inbound webhook, and a January 2026 advisory shows what a missing one costs. Why an agent behind the endpoint raises the stakes.
By Harinderpal Hanspal on October 2026
A webhook endpoint that does not verify its sender accepts orders from anyone who finds the URL. Stripe's documentation says forged events can trigger fulfillment and access changes. An agent behind that endpoint acts on them with its own access.
Stripe's webhook documentation does not soften it. Without verification, an attacker can send fake events to the endpoint to trigger actions such as fulfilling orders, granting account access or modifying records (Stripe).
That is not a thought experiment. In January 2026 the n8n workflow platform published an advisory, CVE-2026-21894, for its Stripe Trigger node. The node had created and stored a signing secret when it registered the webhook, but incoming requests were never checked against it. The record says any client that knew the webhook URL could start a workflow as if a real Stripe event had arrived, and names fake payment or subscription events as the possible effect. It also says the practical risk was reduced because the URL held a long random identifier, which authenticated users of the platform could still see. Versions from 0.150.0 up to 2.2.2 were affected, and 2.2.2 fixed it (NVD; GitHub advisory).
The secret existed. Nobody used it.
Four checks for an inbound webhook
A URL is a weak secret. It shows up in logs, dashboards and screenshots. The documentation from Stripe, Slack and GitHub, and OWASP's cheat sheet, converge on the same four checks.
| Check | What it stops | Where it is documented |
|---|---|---|
| Recompute an HMAC over the raw request body with a shared secret | Forged or altered events | Stripe, GitHub, Slack, OWASP |
Compare signatures in constant time, never with == |
Timing attacks on the comparison | GitHub, Stripe, OWASP |
| Reject a timestamp outside a short window | Replay of a captured, validly signed delivery | Stripe (5 minutes by default), Slack (5 minutes in its example), OWASP |
| Remember event IDs already processed | Duplicates inside the window and across retries | Stripe, OWASP |
Two details trip people up. Stripe needs the raw body, so a framework that parses and re-serializes the JSON first breaks verification. And not every provider signs a timestamp: OWASP points out that GitHub's signature covers the raw body only, so a captured GitHub delivery stays valid, and the receiver has to make the work it triggers safe to repeat.
Stripe adds a warning about its own tolerance setting: a value of zero turns the recency check off.
Why an agent behind the webhook raises the stakes
A handler that only updates a row limits the damage to a wrong row. An agent behind the same endpoint decides what to do next, then does it with whatever tools and credentials it holds.
Picture a maintenance system that posts "work order 4417 closed" to an agent that reschedules the crew, orders the part and notifies the shift lead. Whoever can post that message gets all three actions, and the agent's own access pays for them. The sender's claimed identity became the agent's trust, with no human reading the message in between.
Network location does not fix this. Stripe tells receivers to use both an IP allowlist and signature verification, not one of them.
Questions to put to a vendor about inbound webhooks
- Is the signature verified on every inbound webhook, and what does the endpoint return when it is missing or wrong?
- Is the comparison constant-time, and is it done on the raw body?
- Is there a timestamp window, and what happens when the sender's scheme has no timestamp?
- Are event IDs stored, so a replay or retry cannot trigger the same action twice?
- When a webhook starts an agent, which tools can that agent reach, and does the sender's identity limit them?
- Can a signing secret be rotated without downtime, and who is told when it changes?
A vendor who answers the first question with "the URL is unguessable" has told you the secret is the address.
Drawn from the webhook documentation of Stripe, GitHub and Slack, the OWASP Webhook Security Cheat Sheet, and the NVD record and GitHub advisory for CVE-2026-21894 (January 2026), all read on 6 October 2026. The reasoning about agents is ours.
Related notes
- An AI agent log can prove nothing changed and still not say who acted
- Test AI-written code by what a silent failure would cost
Related paper: Governing agents in production: what to ask before an agent acts