Integration · Webhooks

Webhook Security 2026 — A Field Guide

A webhook endpoint is a public URL that performs privileged actions on request. Treat every incoming payload as hostile until its signature proves otherwise.

John Kihiu12 min read

Your webhook endpoint is on the public internet, and anyone who finds the URL can POST to it. If the handler creates orders, moves money, or changes account state, an unverified endpoint is a remote-control button for attackers. Security here is about proving each request genuinely came from the provider — and refusing everything that cannot.

Verify the signature

Reputable providers sign each webhook: they compute an HMAC of the raw request body with a shared secret and put it in a header. You recompute the HMAC on the exact bytes you received and compare. If it does not match, reject with a 4xx and do nothing else. This is the single most important control — without it, nothing else matters.

Python · HMAC signature verification
import hmac, hashlib

def verify(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)  # constant-time compare

Two details people get wrong: verify against the raw body, not a re-serialised version — reparsing and re-encoding the JSON changes the bytes and breaks the signature. And use a constant-time comparison (hmac.compare_digest), because a normal string compare leaks timing information an attacker can exploit.

Block replays with a timestamp

A captured valid request can be re-sent by an attacker — a replay. Providers include a timestamp in the signed payload; reject requests whose timestamp is outside a small tolerance (say five minutes), so an old captured request is stale by the time it is replayed. Combine this with event-id dedupe and a replayed request is both stale and already-seen.

Harden the rest

Signature verification is not optional

Every other control is defence in depth around this one check. An endpoint that skips signature verification is trusting the entire internet to only send legitimate events. If a provider does not sign its webhooks, put them behind a shared-secret path token at minimum, and push the provider to add signing.

Secure webhook handling is a short, strict pipeline: TLS in, raw-body HMAC verification, timestamp and dedupe checks against replay, then bounded validation of the contents. Everything that fails any step is rejected before it touches your business logic — which is exactly where you want the line drawn.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.