atokim4/stripe-webhook-security

Verifica e corrige um endpoint de webhook do Stripe. Escaneia manipuladores que atuam em eventos do Stripe sem verificação de assinatura via `constructEvent` e assinaturas verificadas em relação a um corpo analisado (`req.json()`, `JSON.stringify`, `express.json()`, uma rota pages/api com o analisador padrão), e então orienta sobre corpo bruto, idempotência, repetição e tratamento do segredo. Use quando o usuário escreve ou revisa um webhook do Stripe, pergunta "meu webhook do Stripe é seguro?", obtém "Nenhuma assinatura encontrada correspondente à assinatura esperada" ou antes de lançar um aplicativo que processa pagamentos.

O que é stripe-webhook-security?

Esta skill executa a parte Stripe do scanner vibe-preflight (`scripts/preflight.mjs`), que não requer dependências nem rede. Ela escaneia webhooks em roteadores Next.js (App Router e pages/api), Express e Supabase Edge Functions, verificando se os manipuladores utilizam `constructEvent` para validar a assinatura e se o corpo da requisição foi analisado antes da verificação. A skill também oferece uma revisão manual detalhada de conceitos como tratamento do segredo, prevenção de repetição (replay), idempotência, ordem de eventos, confiança nos dados do evento, resposta rápida e lista de eventos permitidos. Suportada por Claude Code (preenche `${CLAUDE_SKILL_DIR}`) e outros agentes que apontem para a pasta da skill, ela executa o comando `node scripts/preflight.mjs . --only stripe --fail-on warning --format json` para identificar problemas e orientar a correção com exemplos de código para cada plataforma. Após as correções, a skill recomenda reexecutar o scanner e testar com o Stripe CLI.

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Segurança do webhook do Stripe

Esta skill executa a parte Stripe do scanner vibe-preflight (scripts/preflight.mjs ao lado deste arquivo, Node 20+, sem dependências, sem rede). Ela encontra manipuladores de webhook em roteadores Next.js, pages/api, Express e Supabase Edge Functions e verifica duas coisas no código. O resto desta página é uma revisão que você faz lendo o manipulador, porque uma varredura de padrões não pode provar isso.

1. Execute a varredura

A partir da raiz do projeto:

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

Claude Code preenche ${CLAUDE_SKILL_DIR}. Outros agentes: use a pasta desta skill, por exemplo .agents/skills/stripe-webhook-security ou .claude/skills/stripe-webhook-security.

RegraNívelO que significa
stripe-webhook-unverifiederroO manipulador atua em eventos do Stripe e nunca chama constructEvent / constructEventAsync. Qualquer um que encontrar a URL pode postar um checkout.session.completed falso.
stripe-webhook-parsed-bodyavisoA assinatura é verificada, mas em relação a um corpo que já foi analisado. O Stripe assina exatamente os bytes que enviou, então eventos reais falham com 400 e alguém acaba removendo a verificação.

--only stripe encontra um manipulador por nomes de eventos do Stripe, pela leitura do cabeçalho stripe-signature ou por um caminho de webhook em um arquivo que menciona Stripe. Se relatar 0 erros mas o usuário sabe que tem um webhook, encontre o manipulador você mesmo e faça a revisão abaixo de qualquer maneira.

2. Corrija a verificação

Manipulador de rota (Next.js App Router, Remix, Hono e outros manipuladores estilo fetch):

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

Express: registre a rota do webhook com express.raw({ type: "application/json" }) antes de app.use(express.json()), e passe req.body (que será um Buffer) para constructEvent.

Next.js pages/api: export const config = { api: { bodyParser: false } }; e leia a requisição bruta em um Buffer antes de verificar.

Supabase Edge Functions e outros runtimes Deno / edge: await stripe.webhooks.constructEventAsync(body, signature, secret, undefined, Stripe.createSubtleCryptoProvider()).

3. Revise o restante lendo o manipulador

O scanner não verifica estes itens. Olhe o código e informe ao usuário o que você encontrou para cada um.

  • Segredo. STRIPE_WEBHOOK_SECRET (whsec_...) vem das variáveis de ambiente do servidor, nunca de uma variável NEXT_PUBLIC_ / VITE_ ou do código-fonte. O modo de teste e o modo ao vivo têm segredos diferentes, assim como cada endpoint e o CLI stripe listen.
  • Repetição. constructEvent rejeita uma assinatura mais antiga que sua tolerância (300 segundos por padrão). Deixe o argumento de tolerância de fora ou mantenha-o pequeno. Nunca capture o erro e prossiga.
  • Idempotência. O Stripe pode entregar o mesmo evento mais de uma vez. Torne a escrita naturalmente repetível (um upsert usando o ID do Stripe como chave, definindo um status em vez de adicionar créditos) ou armazene event.id em uma tabela com uma restrição única e ignore os IDs já vistos. Examine com cuidado qualquer coisa que adicione: créditos, saldo, estoque.
  • Ordem. Os eventos podem chegar fora de ordem. Para assinaturas, busque o objeto atual da API (stripe.subscriptions.retrieve) ou compare timestamps em vez de confiar na ordem dos eventos.
  • Confiança. Extraia o usuário e o plano do evento verificado (client_reference_id, metadata, o ID do cliente que você armazenou no checkout), nunca de uma string de consulta ou de uma segunda requisição não assinada. O redirecionamento da página de sucesso não prova nada: conceda o acesso a partir do webhook.
  • Resposta. Retorne 2xx rapidamente assim que o evento for armazenado ou tratado. Trabalho lento vai para uma fila. Retorne 400 apenas para uma assinatura inválida, para que as tentativas do Stripe continuem significativas.
  • Lista de eventos. Inscreva o endpoint apenas nos eventos que o código trata, e ignore outros tipos sem erro.

4. Relatório

Liste os achados do scanner, depois uma linha por item da revisão: ok, corrigido ou uma pergunta para o usuário (por exemplo "créditos são adicionados em cada entrega: um evento repetido deve adicioná-los novamente?"). Execute o scanner novamente após a correção e mostre sua última linha. Para testar de verdade, sugira stripe listen --forward-to localhost:3000/api/webhooks e stripe trigger checkout.session.completed com o Stripe CLI.

Um falso positivo confirmado pode ser silenciado com // vibe-preflight-ignore <rule-id> na linha ou na linha acima dela, com o motivo no comentário.

Individual skills in this repo

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

Habilidades Relacionadas