Webhooks Explicados: Payloads Assinados e Estratégias de Retry
Você acabou de integrar um serviço de terceiros que envia webhooks, e tudo funciona bem nos testes. Mas em produção, você percebe eventos ocasionais perdidos, entregas duplicadas ou, pior, um ator malicioso enviando payloads falsos para o seu endpoint. Webhooks são simples em conceito, mas complicados na prática. Este artigo explica como proteger webhooks com payloads assinados e implementar estratégias de retry robustas para que você possa confiar nas suas integrações orientadas a eventos.
O que são Webhooks e por que são importantes
Webhooks são callbacks HTTP definidos pelo usuário, acionados por eventos específicos. Quando um evento ocorre — como um pagamento bem-sucedido ou um novo cadastro de usuário — o serviço de origem envia uma requisição HTTP POST para uma URL que você fornece. Esse modelo baseado em push é mais eficiente do que polling, reduzindo a latência e a carga do servidor. No entanto, introduz desafios: verificar a autenticidade e lidar com falhas de entrega.
Protegendo Webhooks com Payloads Assinados
Sem verificação, qualquer pessoa pode enviar requisições para o seu endpoint de webhook. Payloads assinados resolvem isso incluindo uma assinatura criptográfica que prova que a requisição se originou do remetente confiável.
Como funcionam as assinaturas HMAC
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 (por exemplo, X-Signature). Seu servidor recalcula o hash e compara. Se forem iguais, a requisição é autêntica.
// Exemplo: Verificando uma assinatura de webhook em Node.js
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const digest = hmac.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
Importante: Sempre use uma comparação com tempo constante para evitar ataques de temporização. Além disso, verifique a assinatura contra o corpo bruto da requisição, não um objeto JSON analisado, porque a análise pode alterar o payload.
Melhores práticas para verificação de assinatura
- Use HTTPS: Sempre sirva webhooks sobre TLS para evitar interceptação.
- Rotacione segredos: Rotacione periodicamente seu segredo de assinatura e suporte múltiplos segredos ativos durante a rotação.
- Valide o timestamp: Inclua um timestamp na assinatura para evitar ataques de replay. Rejeite requisições mais antigas que alguns minutos.
- Registre falhas: Monitore falhas de verificação de assinatura para detectar ataques ou configurações incorretas.
Implementando Retries Confiáveis
Mesmo com payloads assinados, problemas de rede ou indisponibilidades temporárias podem fazer com que a entrega do webhook falhe. Uma estratégia de retry robusta garante que os eventos sejam eventualmente entregues sem sobrecarregar seu sistema.
Backoff Exponencial com Jitter
Tentar novamente imediatamente pode piorar o congestionamento. Em vez disso, use backoff exponencial: espere mais tempo entre cada tentativa. Adicione jitter (variação aleatória) para evitar problemas de manada quando muitos webhooks falham simultaneamente.
// Exemplo: Agendamento de retry com backoff exponencial e jitter
function getRetryDelay(attempt) {
const base = 1000; // 1 segundo
const max = 30000; // 30 segundos
const delay = Math.min(base * Math.pow(2, attempt), max);
return delay + Math.random() * 1000; // adiciona até 1s de jitter
}
Idempotência: Lidando com Duplicatas
Retries podem causar entregas duplicadas. Seu manipulador de webhook deve ser idempotente — processar o mesmo evento várias vezes deve ter o mesmo efeito que processá-lo uma vez. Use o ID do evento do payload para rastrear eventos processados.
- Armazene IDs de eventos processados em um banco de dados com TTL.
- Antes de processar, verifique se o ID do evento já existe; se sim, retorne uma resposta de sucesso imediatamente.
- Projete operações para serem idempotentes (por exemplo, use upserts em vez de inserts).
Responda Rapidamente e Confirme
Seu endpoint de webhook deve responder com um código de status 2xx o mais rápido possível — idealmente dentro de alguns segundos. Se o processamento demorar mais, coloque o evento em fila para tratamento assíncrono e retorne 202 Accepted. Isso evita timeouts e retries desnecessários.
Comparação: Webhook vs. Polling
| Aspecto | Webhooks | Polling |
|---|---|---|
| Latência | Quase tempo real | Depende do intervalo |
| Carga do Servidor | Baixa (apenas push) | Alta (requisições constantes) |
| Complexidade | Maior (precisa de retries, segurança) | Menor |
| Confiabilidade | Requer lógica de retry | Inerentemente confiável |
Passo a Passo: Construindo um Receptor de Webhook Seguro
- Configure um endpoint HTTPS com um certificado válido.
- Extraia o cabeçalho de assinatura (por exemplo,
X-Signature) e o timestamp. - Verifique a assinatura usando o segredo compartilhado e o corpo bruto.
- Verifique o timestamp para rejeitar requisições antigas (por exemplo, mais de 5 minutos).
- Analise o payload e verifique IDs de eventos duplicados.
- Processe o evento de forma assíncrona, se necessário, e responda com 200 OK.
- Registre o evento para auditoria e depuração.
FAQ
O que é um payload de webhook assinado?
Um payload assinado inclui uma assinatura criptográfica (geralmente HMAC) que permite ao receptor verificar que a requisição veio de uma fonte confiável e não foi adulterada.
Quantas vezes devo tentar novamente um webhook com falha?
A maioria dos sistemas tenta novamente de 3 a 5 vezes com backoff exponencial. Depois disso, o evento geralmente é marcado como falho e pode exigir intervenção manual ou tratamento de fila de mensagens mortas.
Posso usar webhooks sem HTTPS?
É fortemente desencorajado. Sem HTTPS, payloads e assinaturas podem ser interceptados ou modificados. Sempre use TLS para endpoints de webhook.
Ao depurar payloads ou logs de webhook, formatar o JSON corretamente é essencial. Use nosso Formatador JSON para imprimir e validar payloads JSON rapidamente.