FixVibe

// código / spotlight

Webhook Signature Verification

If your webhook handler doesn't verify the signature, anyone can forge events.

El gancho

Webhook signature verification is one of the most consistently-skipped steps in integration code, and the reason is structural: the integration works fine without it. The webhook arrives, the body parses, the handler runs, the test passes. Verification is the step you only notice when an attacker posts a forged event to your endpoint. Stripe, GitHub, Clerk, Resend, Linear, Slack, Vercel, and every other service worth integrating with sets a signature header — `Stripe-Signature`, `X-Hub-Signature-256`, `svix-id`, etc. — and explicitly tells you to verify before trusting the payload. The instructions are in the documentation. The skipping is in the codebase.

Cómo funciona

Each provider computes an HMAC over the raw request body (using a secret you configure on their side), serializes the result into a header, and sends both. Your handler is supposed to: read the raw body (not the parsed body — parsing changes whitespace and breaks the HMAC), recompute the HMAC using your shared secret, compare with the header using a timing-safe equality function, then trust the body. Skip the check and you accept any payload from any caller — Stripe's webhook URL is not authenticated by IP, secret token in URL, or any other mechanism; the signature is the only authentication. The mistakes cluster in three shapes: never adding the verification call (the handler trusts everything), parsing the body before verifying (the HMAC fails because the parsed-and-reserialized body has different whitespace), and using non-timing-safe comparison (`===` against the signature is technically right but timing-leaks the secret bit-by-bit).

Las variantes

No signature check at all

Handler parses and trusts every incoming POST. Most common shape; trivially exploitable by anyone who knows the URL.

Verify-after-parse

Handler parses body to JSON, then computes HMAC over the JSON.stringify version. Whitespace differs from the original; HMAC fails. Verification 'works' for legit events because both sides round-trip the same way, but is broken for production-grade attackers.

Non-timing-safe comparison

`if (signature === expectedSignature)` instead of `crypto.timingSafeEqual(...)`. Leaks one bit per request through timing analysis. Less common in practice but a genuine vulnerability.

Replay without timestamp check

Even with HMAC verification, accepting old timestamps lets an attacker replay captured events. Stripe's signature includes a timestamp; rejecting events older than 5 minutes defeats replay.

El radio de impacto

Forged events. Stripe webhook fakery: an attacker posts a fake `payment_intent.succeeded` event to your `/api/stripe/webhook` endpoint, your handler marks the order as paid, you ship goods you weren't paid for. GitHub webhook fakery: fake `release.published` event triggers your CI to deploy attacker-controlled artifacts. Clerk webhook fakery: fake `user.created` event seeds a malicious user into your database with admin role. The pattern is identical across providers — without signature verification, the webhook endpoint is an unauthenticated mutation API.

// what fixvibe checks

What FixVibe checks

FixVibe repo scans look for high-confidence security patterns and dependency risk in source context. Reports identify the affected area and recommended fix. For check-specific questions about exact detection heuristics, active payload details, or source-code rule patterns, contact support@fixvibe.app.

Defensas a prueba de balas

Always verify signatures before parsing or trusting any body field. Use the official SDK helper when available — `stripe.webhooks.constructEvent(rawBody, sig, secret)` for Stripe, GitHub's documented HMAC SHA-256 over rawBody compared with `crypto.timingSafeEqual`, `svix.verify(rawBody, headers)` for Svix-shaped providers (Resend, Clerk, Linear). Critical: read the raw body before parsing — `req.text()` in Next.js, `bodyParser.raw()` in Express, `express.raw({ type: '*/*' })` middleware, or framework-specific raw-body access. Reject requests without signatures, even in test environments; sloppiness in test becomes sloppiness in prod. Validate the timestamp included in the signature (Stripe's tolerance is 5 minutes by default) so old captured events can't be replayed. Store webhook event IDs after processing and refuse re-processing the same ID — handles legitimate retries without enabling deliberate replay. As a final layer, scope the webhook secret per-provider per-environment so a leak is contained.

La conclusión

Signature verification is the difference between a webhook endpoint that's part of a trusted integration and one that's a public mutation API. The verification call is one line. Skipping it is the kind of bug you find in a postmortem rather than a code review.

// ejecútalo en tu propia app

Sigue lanzando mientras FixVibe vigila.

FixVibe somete la superficie pública de tu app a la misma presión que un atacante — sin agente, sin instalación, sin tarjeta. Seguimos investigando nuevos patrones de vulnerabilidad y los convertimos en checks prácticos y fixes listos para Cursor, Claude y Copilot.

Código fuente
52
tests en esta categoría
módulos
14
checks dedicados de código fuente
cada scan
384+
tests en todas las categorías
  • Gratis — sin tarjeta, sin instalación, sin ping de Slack
  • Solo pega una URL — nosotros crawleamos, sondeamos y reportamos
  • Hallazgos clasificados por severidad, deduplicados al puro signal
  • Prompts de fix actuales, listos para Cursor, Claude, Copilot
Ejecutar un escaneo gratis

// checks actuales · fixes prácticos · lanza con confianza

Webhook Signature Verification — Spotlight de Vulnerabilidad | FixVibe · FixVibe