CommunityProgramación y desarrollogithub.com

atokim4/stripe-webhook-security

Verifica y corrige un endpoint de webhook de Stripe. Escanea manejadores que procesan eventos de Stripe sin verificar la firma con `constructEvent` y aquellos que verifican la firma contra un cuerpo analizado (`req.json()`, `JSON.stringify`, `express.json()`, rutas pages/api con el analizador por defecto), y luego revisa el manejo del cuerpo sin procesar, la idempotencia, la repetición y el secreto. Úsalo cuando el usuario escribe o revisa un webhook de Stripe, pregunta "¿es seguro mi webhook de Stripe?", recibe el error "No signatures found matching the expected signature", o antes de lanzar una aplicación que procesa pagos.

¿Qué es stripe-webhook-security?

Este skill automatiza la verificación de seguridad de webhooks de Stripe en proyectos que usan Next.js, Express, Supabase Edge Functions y otros entornos. Escanea el código en busca de dos problemas críticos: manejadores que procesan eventos de Stripe sin verificar la firma (`constructEvent`/`constructEventAsync`), y verificaciones realizadas contra un cuerpo que ya ha sido analizado por un parser JSON, lo que puede provocar que eventos legítimos fallen. Además, proporciona una guía detallada para la revisión manual de aspectos como el manejo del secreto del webhook, protección contra repeticiones, idempotencia, orden de eventos, confianza en los datos recibidos, tiempo de respuesta del endpoint y la lista de eventos suscritos. El skill incluye un script de escaneo preflight que se ejecuta desde la raíz del proyecto, sin dependencias ni red, y produce resultados en formato JSON. Es especialmente útil para revisar la seguridad de webhooks existentes, diagnosticar el error común 'No signatures found matching the expected signature', o realizar una revisión de seguridad antes del lanzamiento de una aplicación que maneja pagos.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/atokim4/vibe-preflight/tree/HEAD/skills/stripe-webhook-security

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Seguridad de webhooks de Stripe

Este skill ejecuta la parte de Stripe del escáner vibe-preflight (scripts/preflight.mjs junto a este archivo, Node 20+, sin dependencias, sin red). Encuentra manejadores de webhook en manejadores de ruta de Next.js, pages/api, Express y Supabase Edge Functions y verifica dos cosas en el código. El resto de esta página es una revisión que realizas leyendo el manejador, porque un escaneo de patrones no puede probarlo.

1. Ejecutar el escaneo

Desde la raíz del proyecto:

node ${CLAUDE_SKILL_DIR}/scripts/preflight.mjs . --only stripe --fail-on warning --format json

Claude Code completa ${CLAUDE_SKILL_DIR}. Otros agentes: usa la carpeta de este skill, por ejemplo .agents/skills/stripe-webhook-security o .claude/skills/stripe-webhook-security.

ReglaNivelSignificado
stripe-webhook-unverifiederrorEl manejador actúa sobre eventos de Stripe y nunca llama a constructEvent / constructEventAsync. Cualquier persona que encuentre la URL puede enviar un checkout.session.completed falso.
stripe-webhook-parsed-bodywarningLa firma se verifica, pero contra un cuerpo ya analizado. Stripe firma los bytes exactos que envió, por lo que los eventos reales fallan con 400 y alguien termina eliminando la verificación.

--only stripe encuentra un manejador por nombres de eventos de Stripe, una lectura del encabezado stripe-signature, o una ruta de webhook en un archivo que menciona Stripe. Si reporta 0 errors pero el usuario sabe que tiene un webhook, encuentra el manejador tú mismo y realiza la revisión a continuación de todas formas.

2. Corregir la verificación

Manejador de ruta (Next.js App Router, Remix, Hono y otros manejadores tipo fetch):

export async function POST(request: Request) {
  const body = await request.text(); // cuerpo sin procesar, nunca request.json()
  const signature = request.headers.get("stripe-signature");
  if (!signature) return new Response("Falta la firma", { status: 400 });
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch {
    return new Response("Firma inválida", { status: 400 });
  }
  // manejar evento
  return Response.json({ received: true });
}

Express: registra la ruta del webhook con express.raw({ type: "application/json" }) antes de app.use(express.json()), y pasa req.body (un Buffer entonces) a constructEvent.

Next.js pages/api: export const config = { api: { bodyParser: false } }; y lee la solicitud sin procesar en un Buffer antes de verificar.

Supabase Edge Functions y otros entornos Deno / edge: await stripe.webhooks.constructEventAsync(body, signature, secret, undefined, Stripe.createSubtleCryptoProvider()).

3. Revisar el resto leyendo el manejador

El escáner no verifica estos puntos. Mira el código e informa al usuario lo que encontraste para cada uno.

  • Secreto. STRIPE_WEBHOOK_SECRET (whsec_...) proviene de variables de entorno del servidor, nunca de una variable NEXT_PUBLIC_ / VITE_ o del código fuente. El modo de prueba y el modo en vivo tienen diferentes secretos, al igual que cada endpoint y la CLI stripe listen.
  • Repetición. constructEvent rechaza una firma más antigua que su tolerancia (300 segundos por defecto). Omite el argumento de tolerancia o mantenlo pequeño. Nunca captures el error y continúes.
  • Idempotencia. Stripe puede entregar el mismo evento más de una vez. Haz que la escritura sea naturalmente repetible (un upsert con la clave del id de Stripe, estableciendo un estado en lugar de agregar créditos) o almacena event.id en una tabla con una restricción única y omite los ids que ya hayas visto. Revisa cuidadosamente cualquier cosa que agregue: créditos, saldo, stock.
  • Orden. Los eventos pueden llegar desordenados. Para suscripciones, obtén el objeto actual de la API (stripe.subscriptions.retrieve) o compara marcas de tiempo en lugar de confiar en el orden de los eventos.
  • Confianza. Toma el usuario y el plan del evento verificado (client_reference_id, metadata, el id de cliente que almacenaste al finalizar la compra), nunca de una cadena de consulta o una segunda solicitud sin firma. La redirección a la página de éxito no prueba nada: otorga acceso desde el webhook.
  • Respuesta. Devuelve un 2xx rápidamente una vez que el evento se almacena o maneja. El trabajo lento va a una cola. Devuelve 400 solo para una firma incorrecta, para que los reintentos de Stripe sigan siendo significativos.
  • Lista de eventos. Suscribe el endpoint solo a los eventos que el código maneja e ignora otros tipos sin error.

4. Reporte

Enumera los hallazgos del escáner, luego una línea por elemento de revisión: bien, corregido, o una pregunta para el usuario (por ejemplo "los créditos se agregan en cada entrega: ¿un evento repetido debería agregarlos de nuevo?"). Vuelve a ejecutar el escaneo después de la corrección y muestra su última línea. Para probar en real, sugiere stripe listen --forward-to localhost:3000/api/webhooks y stripe trigger checkout.session.completed con la CLI de Stripe.

Un falso positivo confirmado puede silenciarse con // vibe-preflight-ignore <rule-id> en la línea o la línea anterior, con la razón en el comentario.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills relacionados