← Back to Research

The unverified callback: when your webhook believes anyone who knocks

The shape that matters. An inbound webhook is an unauthenticated endpoint by default. The only thing standing between it and a forged event is a signature check you have to write, and CVE-2026-39969 is what happens when that check is missing. The provider signs every delivery; the receiver just never looks.

Webhooks are how modern apps stitch themselves together: a payment processor tells you a charge succeeded, a messaging platform tells you a customer replied, a CI system tells you a build finished. Each is an HTTP request your server receives from someone else's server. And here is the uncomfortable truth that this week's CVE makes concrete: an inbound webhook endpoint is, by construction, a publicly reachable URL that will act on whatever it receives, unless you verify that the sender is who they claim to be.

CVE-2026-39969: a webhook that trusts the knock, not the key

CVE-2026-39969 (CVSS 6.5) is in TypeBot, an open-source chatbot builder, in versions 3.16.0 and prior. Its WhatsApp Cloud API webhook endpoint (POST /v1/workspaces/{workspaceId}/whatsapp/{credentialsId}/webhook) does not verify the x-hub-signature-256 HMAC that the platform includes on every single delivery. The provider does its part: it signs the payload with a shared secret and ships the signature in a header. The receiver simply never checks it.

It gets worse because of where the identifiers live. The webhook URL carries both workspaceId and credentialsId as path parameters. Path parameters end up in web server access logs, in proxy logs, in browser history, in referrer headers: anywhere a URL is recorded. So the “secret” URL is not secret for long. Once an attacker learns it (and URLs leak constantly), they can forge inbound WhatsApp events: fake messages, fake delivery statuses, fake conversation state: anything the legitimate platform could send, now spoofable by anyone.

This is the entire bug class in one example. The mechanism is “trust the request because it arrived,” and the fix is “trust the request because it was signed.”

The same failure wears many costumes

Signature-skipping is the most common shape, but the “inbound integration endpoint that trusts too much” class shows up several ways:

Worth naming alongside this is a related “test mode that forgot it was production” pattern we have seen elsewhere this period: tools that expose a script-test or debug execution path with an authorization gate that does not actually gate, turning a convenience feature into unauthenticated execution. The common thread with the webhook bug is the same: an endpoint reachable without proof of identity, doing work it should only do for a trusted caller.

The defensible test

You verify your own webhook posture by confirming the receiver rejects an unsigned or wrongly-signed request, against your own endpoint, with your own secret. You never forge events against someone else's integration.

# Webhook signature posture: against YOUR endpoint, YOUR secret.
# Asserts the receiver rejects a request that lacks a valid HMAC.
URL="https://app.example/v1/.../webhook"
BODY='{"object":"whatsapp_business_account","entry":[]}'

# 1) No signature header at all: must be rejected (401/403), not 200.
curl -s -o /dev/null -w 'no-sig -> %{http_code}\n' -X POST "$URL" \
  -H 'Content-Type: application/json' -d "$BODY"

# 2) Wrong signature: must be rejected.
curl -s -o /dev/null -w 'bad-sig -> %{http_code}\n' -X POST "$URL" \
  -H 'Content-Type: application/json' \
  -H 'x-hub-signature-256: sha256=deadbeef' -d "$BODY"

# FINDING if either returns 2xx / is acted upon. The endpoint must demand a
# valid HMAC over the raw body before doing anything.

The fix, in order of importance

How Celvex Sentry tests for this

Our continuous-monitoring suite probes inbound integration endpoints for the missing- or bypassable-signature class, sending unsigned and wrongly-signed requests against customer-owned endpoints to confirm they are rejected, and flagging identifiers-as-secrets in webhook URLs. When an endpoint acts on a forgeable request, we mint a Proof Capsule with the rejected-vs-accepted evidence and the verification fix attached. When the HMAC gate holds and replay is bounded, we record a PASS.

Sources

Get your exposure check: full report in 4-24 hours

Real assessment on production-grade infrastructure. We prove what is exploitable and attach the fix. Paying customers get priority capacity.

Queue My Assessment