How ZestyGlue verifies every webhook

The signing scheme each source uses, why timing-safe comparison matters, and what happens when a signature doesn't match.

Why verify at all

A webhook URL is, by itself, just a bearer secret — anyone who learns it (a leaked log line, a misconfigured proxy, a browser history sync) can POST anything they want to it. Without verification, a receiving endpoint has no way to distinguish a genuine event from a forged one: a fabricated refund, a fake "deployment succeeded" ping, an invented support ticket. Signature verification closes that gap by requiring every request to prove it was generated by someone who holds a secret only the real source and ZestyGlue know — the URL alone is no longer enough.

Signing schemes by source

Most providers sign with HMAC — a cryptographic hash of the request body keyed with a shared secret, sent alongside the request in a header. ZestyGlue verifies whichever scheme the source actually uses, rather than forcing every source into one shape:

SchemeSources
HMAC-SHA256 (body)Stripe, Shopify, WooCommerce, Lemon Squeezy, Paddle, Ghost, Tally, Typeform, Calendly, Cal.com, DocuSign, Twitch, GitHub, Sentry
HMAC-SHA256 (notification URL + body)Square — the one source here that signs over more than just the request body
HMAC-SHA1Vercel — verified against Vercel's own docs; it's the one source that isn't SHA256
HMAC-MD5Patreon — also verified against Patreon's docs; unusual, but genuinely what they use
Static tokenGitLab, Ko-fi, Supabase — a plain shared secret compared directly, rather than a computed signature
None availableAny Webhook, PayPal, Gumroad, Jotform, UptimeRobot, Docker Hub — these rely on the webhook URL itself being unguessable

Where a source offers no signature scheme at all, the only real mitigation is that the URL is a long, random, effectively unguessable path — the same reason you wouldn't publish a webhook URL publicly even for a source that does support signing.

Timing-safe comparison

Computing the right signature is only half the job — how it's compared against the one the source sent matters too. A naive comparison (Python's == on two strings) exits as soon as it hits the first mismatched character, which means a mismatch on character 1 returns fractionally faster than a mismatch on character 30. Given enough requests and precise enough timing measurement, that difference is a real, if slow, side channel an attacker could use to guess a signature byte by byte. Every signature check in ZestyGlue uses hmac.compare_digest, which takes the same amount of time regardless of where — or whether — the strings differ, closing that channel off entirely.

What happens when verification fails

The event is logged as rejected in your connector's event log and dropped — nothing is forwarded to your destination. Critically, the source still receives a normal 200 OK response. Returning an error here would let an attacker probing a webhook URL learn, from the response alone, that signature verification exists and is failing — information they could use to iterate toward a valid signature or simply confirm the endpoint is worth attacking further. A flat 200 gives no such feedback, at the cost of the source never being told a specific delivery was rejected — which is also why every connector's own event log, not the source's dashboard, is the right place to check when something isn't arriving. See the troubleshooting section of the docs for what each event-log status actually means.

Where your secrets are stored

Signing secrets, API tokens, and destination webhook URLs are encrypted at rest with Fernet symmetric encryption before they ever touch disk, decrypted only in memory for the moment a connector fires, and never logged in plaintext anywhere. Once you save a connector's credentials, the dashboard never displays the real value again — only a masked placeholder — so even a compromised session on your own account can't be used to exfiltrate a secret you already entered.

Questions about a specific source's scheme, or think we've got a detail wrong? Get in touch — this page describes the actual verification code running in production, not a general description of how HMAC works, so we'd genuinely want to know if it drifts out of sync.