Webhooks Explicados: Payloads Assinados e Estratégias de Retry
Você acabou de construir um endpoint de webhook para receber eventos de um serviço de terceiros. Tudo funciona nos testes, mas em produção você percebe que alguns eventos nunca chegam ou, pior, recebe eventos duplicados causando inconsistências nos dados. Esses são problemas clássicos de webhooks que derivam de dois aspectos críticos: autenticidade do payload e confiabilidade da entrega.
Webhooks são callbacks HTTP acionados por eventos, que enviam dados para sua aplicação em tempo real. Diferente do polling, eles são eficientes e quase instantâneos. Mas introduzem desafios: como saber se a requisição realmente veio do remetente esperado e como lidar com falhas quando o receptor está temporariamente indisponível? Este artigo explora payloads assinados e mecanismos de retry, fornecendo padrões práticos para construir integrações de webhook resilientes.
Por que Webhooks Precisam de Assinaturas
Endpoints de webhook são URLs publicamente acessíveis. Sem verificação, qualquer pessoa pode enviar requisições forjadas para seu endpoint, potencialmente acionando ações não autorizadas ou corrupção de dados. Payloads assinados resolvem isso adicionando uma assinatura criptográfica a cada requisição, permitindo verificar a identidade do remetente e garantir que o payload não foi adulterado.
A maioria dos provedores usa HMAC (Hash-based Message Authentication Code) com um segredo compartilhado. O remetente calcula um hash do payload usando o segredo e o inclui em um cabeçalho. Você recalcula o hash do seu lado e compara. Se corresponderem, a requisição é legítima.
Implementando Verificação HMAC
Vamos percorrer uma implementação típica em Node.js. Suponha que o provedor envie uma assinatura no cabeçalho X-Signature, calculada como HMAC-SHA256 do corpo bruto da requisição usando seu segredo.
const crypto = require('crypto');
const express = require('express');
const app = express();
// Use raw body parser to preserve exact payload
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const expected = crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Process the event
const event = JSON.parse(req.body);
// ... handle event
res.status(200).send('OK');
});
Pontos-chave:
- Sempre use o corpo bruto da requisição para verificação, não o JSON parseado. O parsing pode alterar espaços em branco ou a ordem das chaves, quebrando o hash.
- Use uma comparação timing-safe para prevenir ataques de temporização.
- Mantenha seu segredo seguro; rotacione-o periodicamente e suporte múltiplos segredos ativos durante a rotação.
Projetando uma Estratégia de Retry
Mesmo com assinaturas, entregas podem falhar devido a problemas de rede, indisponibilidade do servidor ou timeouts. Uma estratégia de retry robusta garante a entrega eventual sem sobrecarregar seu sistema ou o remetente.
1. Confirme Rapidamente, Processe Assincronamente
Seu endpoint de webhook deve responder com um status 2xx o mais rápido possível, idealmente em poucos segundos. Se o processamento demorar mais, enfileire o evento para processamento em segundo plano. Isso evita timeouts e permite que o remetente considere a entrega bem-sucedida.
2. Implemente Backoff Exponencial com Jitter
Quando uma entrega falha (resposta não-2xx ou timeout), o remetente deve tentar novamente com atrasos crescentes. Backoff exponencial (ex.: 1s, 2s, 4s, 8s...) evita sobrecarregar seu endpoint. Adicionar jitter aleatório evita problemas de manada quando muitos webhooks falham simultaneamente.
Exemplo de cronograma de retry:
- Tentativa 1: imediata
- Tentativa 2: após 30 segundos
- Tentativa 3: após 2 minutos
- Tentativa 4: após 10 minutos
- Tentativa 5: após 1 hora
Após um número máximo de tentativas (ex.: 5), o remetente deve parar e notificar você (ex.: via e-mail ou dashboard).
3. Lide com Duplicatas usando Idempotência
Retries podem causar entregas duplicadas. Seu endpoint deve ser idempotente: processar o mesmo evento várias vezes deve ter o mesmo efeito que processá-lo uma vez. Use um ID único de evento do payload e armazene-o. Antes de processar, verifique se o ID já foi visto; se sim, retorne 200 sem reprocessar.
Abordagens comuns:
- Restrição de unicidade no banco de dados para o ID do evento.
- Conjunto Redis com TTL para IDs processados recentemente.
- Chaves de idempotência na sua lógica de negócios.
Comparação: Payloads Assinados vs. Outras Medidas de Segurança
| Método | Prós | Contras |
|---|---|---|
| Assinatura HMAC | Verifica autenticidade e integridade; sem estado | Requer gerenciamento de segredo compartilhado |
| Allowlist de IP | Simples de implementar | IPs podem mudar; não adequado para ambientes dinâmicos |
| Basic Auth | Fácil de adicionar | Credenciais enviadas em cada requisição; menos seguro |
| JWT no Cabeçalho | Pode incluir claims e expiração | Overhead de emissão/validação de token |
Assinaturas HMAC são o padrão de fato porque são sem estado, eficientes e fornecem segurança forte quando implementadas corretamente.
Melhores Práticas para Consumidores de Webhook
- Valide assinaturas em cada requisição, mesmo em desenvolvimento.
- Responda rápido; transfira processamento pesado para uma fila.
- Registre tudo—cabeçalhos, payload e resultado do processamento—para depuração.
- Monitore falhas e configure alertas para erros repetidos.
- Use HTTPS para proteger payloads em trânsito.
- Rotacione segredos regularmente e suporte múltiplos segredos ativos durante a rotação.
Melhores Práticas para Produtores de Webhook
- Assine payloads com um segredo único para cada consumidor.
- Inclua um ID único de evento para auxiliar a idempotência.
- Retente com backoff e limite as tentativas.
- Forneça um dashboard para consumidores visualizarem logs de entrega e reexecutarem eventos.
- Documente seu esquema de assinatura claramente.
Testando sua Implementação de Webhook
Use ferramentas como ngrok para expor seu servidor local e simular entregas de webhook. Escreva testes unitários para verificação de assinatura e lógica de idempotência. Para testes de carga, considere ferramentas que possam enviar requisições assinadas em escala.
Ao depurar, inspecione o payload bruto e os cabeçalhos. Um formatador JSON pode ajudar a visualizar a estrutura do payload. Experimente nosso JSON Formatter para formatar e validar payloads de webhook rapidamente.
FAQ
O que é um payload de webhook assinado?
Um payload assinado inclui uma assinatura criptográfica (geralmente HMAC) que permite ao receptor verificar a identidade do remetente e garantir que o payload não foi alterado. É calculado usando um segredo compartilhado e o corpo da requisição.
Quantas vezes um webhook deve ser retentado?
Não há um número universal, mas a prática comum é de 3 a 5 retentativas com backoff exponencial. Depois disso, o evento é considerado falho e o consumidor deve ser notificado para investigar ou reexecutar manualmente.
Posso usar JWT em vez de HMAC para assinaturas de webhook?
Sim, JWT pode funcionar, mas adiciona complexidade (emissão de token, expiração). HMAC é mais simples e suficiente para a maioria dos casos de uso. Escolha com base na sua infraestrutura existente e requisitos.
Pronto para inspecionar e depurar payloads de webhook? Use nosso JSON Formatter gratuito para formatar e validar JSON instantaneamente.