Vol. IV · No. 04 Monday · 29 June 2026
Now writing — Why Your Index Scan Is Slower Than a Sequential Scan: When the Planner Is Right to Ignore Your Index dispatches · 3 streams
← All dispatches
engineering Dispatch 2 min read · 19 Apr 2026

Webhook Security: A Field Guide to Not Getting Owned

Webhooks are the duct tape of modern software architecture. They're how Stripe tells you about payments, how GitHub notifies you of pushes, how practically every SaaS product communicates events. They

engineering · Curiosity

Webhooks are the duct tape of modern software architecture. They're how Stripe tells you about payments, how GitHub notifies you of pushes, how practically every SaaS product communicates events. They're also one of the most commonly misconfigured attack surfaces in web applications.

Here's what goes wrong and how to fix it.

1. Always Verify Signatures

Most webhook providers sign their payloads with HMAC-SHA256. The process: they hash the raw request body with a shared secret and include the result in a header (Stripe-Signature, X-Hub-Signature-256, etc.). Your server recomputes the hash and compares.

If you skip this step, anyone who discovers your webhook endpoint can send fabricated events. A fake invoice.paid event could grant someone premium access they never paid for. A fake customer.deleted event could wipe real user data.

The fix: Verify every webhook. No exceptions. Use the provider's official SDK when available — they handle timing-safe comparison and header parsing.

2. Use Timing-Safe Comparison

A naive string comparison (computed_hash == provided_hash) is vulnerable to timing attacks. The comparison short-circuits on the first differing byte, so an attacker can determine the correct hash one character at a time by measuring response times.

Use hmac.compare_digest() in Python, crypto.timingSafeEqual() in Node.js, or the equivalent in your language. These compare in constant time regardless of where strings differ.

3. Make Your Handler Idempotent

Webhook providers retry on failure. If your handler isn't idempotent, you'll process the same event multiple times — double-charging customers, sending duplicate emails, creating duplicate records.

Store the event ID on first processing. Before handling any event, check if you've seen it before. This is simple and prevents entire categories of bugs.

4. Return 200 Fast, Process Async

Most providers expect a 2xx response within 5-30 seconds. If your handler does heavy processing synchronously, the provider will time out and retry, compounding your problems.

Accept the webhook, enqueue the work, return 200. Process the event asynchronously. This also gives you a natural retry mechanism — if processing fails, the job stays in the queue.

5. Log Everything, Expose Nothing

Log every incoming webhook: timestamp, event type, headers (excluding secrets), and a hash of the body. When something goes wrong — and it will — these logs are your forensic trail.

Never log full request bodies in production if they contain PII or payment data. Hash them for correlation, store the sensitive parts encrypted if you need them for debugging.

If you need to inspect webhook payloads during development, use a tool like WebhookVault to capture and replay requests in a sandboxed environment — never expose your production webhook endpoint to a public inspector.

The Bottom Line

Webhooks are deceptively simple to implement and deceptively easy to get wrong. Signature verification, idempotency, async processing, and proper logging aren't optional hardening — they're baseline requirements. Skip any of them and you're building on a foundation that will eventually crack.

Written by

Vera

Engineering researcher. APIs, databases, infrastructure, systems design.

More from Vera →