Webhooks Explicados: Payloads Assinados e Estratégias de Retry

Backend2026-10-30TryQuickToolBox

Você construiu uma API e agora precisa notificar outros serviços quando eventos acontecem. Polling é ineficiente, então você implementa webhooks. Mas logo você enfrenta dois problemas comuns: como garantir que o payload não foi adulterado e o que fazer quando o endpoint receptor está fora do ar. Este artigo explica como usar payloads assinados e estratégias de retry para tornar seus webhooks seguros e confiáveis.

Por que Webhooks Precisam de Assinaturas e Retries

Webhooks são callbacks HTTP disparados por eventos. São simples de implementar, mas introduzem desafios de segurança e confiabilidade. Sem assinaturas, um atacante poderia forjar requisições para o seu endpoint, levando à corrupção de dados ou ações não autorizadas. Sem retries, falhas transitórias (como oscilações de rede ou erros temporários de servidor) causam eventos perdidos.

Payloads assinados resolvem a autenticidade: o remetente assina o payload com um segredo, e o receptor o verifica. Retries resolvem a confiabilidade: o remetente tenta novamente entregas falhas com backoff.

Como Funcionam os Payloads Assinados

Um payload assinado inclui uma assinatura criptográfica gerada a partir do corpo da requisição e de um segredo compartilhado. O método mais comum é o HMAC (Hash-based Message Authentication Code) com SHA-256.

Gerando a Assinatura

O remetente calcula o HMAC do corpo bruto da requisição usando a chave secreta. A assinatura é enviada em um header, frequentemente junto com um timestamp para prevenir ataques de replay.

// Exemplo em Node.js: gerando assinatura HMAC
const crypto = require('crypto');

function signPayload(payload, secret) {
  const timestamp = Math.floor(Date.now() / 1000);
  const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
  const signature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  return { timestamp, signature };
}

Inclua o timestamp e a assinatura nos headers, por exemplo, X-Webhook-Timestamp e X-Webhook-Signature.

Verificando a Assinatura

O receptor deve reconstruir o payload assinado usando o timestamp recebido e o corpo bruto, então calcular o HMAC e compará-lo de forma segura.

// Exemplo em Node.js: verificando assinatura
function verifySignature(rawBody, timestamp, receivedSignature, secret) {
  const signedPayload = `${timestamp}.${rawBody}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  // Use comparação timing-safe
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(receivedSignature, 'hex')
  );
}

Sempre use uma comparação timing-safe para prevenir ataques de timing. Além disso, verifique se o timestamp é recente (por exemplo, dentro de 5 minutos) para mitigar ataques de replay.

Projetando uma Estratégia de Retry

Retries são essenciais para uma entrega confiável de webhooks. Mas retries ingênuos podem sobrecarregar o receptor ou causar processamento duplicado. Uma boa estratégia de retry inclui:

Aqui está um loop de retry simples em pseudocódigo:

function deliverWithRetry(url, payload, maxRetries = 5) {
  let attempt = 0;
  while (attempt < maxRetries) {
    try {
      const response = await fetch(url, { method: 'POST', body: payload });
      if (response.ok) return;
    } catch (error) {
      // Log error
    }
    attempt++;
    const delay = Math.pow(2, attempt) * 1000; // backoff exponencial
    await new Promise(resolve => setTimeout(resolve, delay));
  }
  // Mover para dead-letter queue
}

Considere adicionar jitter ao backoff para evitar problemas de thundering herd.

Comparação: Polling vs Webhooks com Retries

Aspecto Polling Webhooks com Retries
Latência Alta (depende do intervalo) Baixa (quase em tempo real)
Carga no servidor Constante (mesmo sem eventos) Orientada a eventos
Confiabilidade Alta (receptor controla) Depende da estratégia de retry
Segurança Autenticação padrão de API Requer payloads assinados

Passo a Passo: Implementando Webhooks com Assinaturas e Retries

  1. Gere uma chave secreta para cada endpoint de webhook e compartilhe-a de forma segura.
  2. Na ocorrência do evento, construa o payload (JSON) e calcule a assinatura usando HMAC-SHA256 com o segredo e um timestamp.
  3. Envie a requisição POST para o endpoint com os headers: Content-Type: application/json, X-Webhook-Timestamp e X-Webhook-Signature.
  4. No lado do receptor, verifique a assinatura usando o corpo bruto e o timestamp. Rejeite se inválida ou se o timestamp for muito antigo.
  5. Se a entrega falhar (resposta não-2xx ou erro de rede), tente novamente com backoff exponencial até um número máximo de tentativas.
  6. Após o máximo de retries, registre a falha e opcionalmente alerte; armazene o evento em uma dead-letter queue para replay manual.
  7. Garanta idempotência no receptor usando um ID de evento para ignorar duplicatas.

Armadilhas Comuns e Boas Práticas

FAQ

O que é um payload de webhook assinado?

Um payload de webhook assinado inclui uma assinatura criptográfica (por exemplo, HMAC) que permite ao receptor verificar que a requisição se originou do remetente esperado e não foi adulterada.

Quantas vezes devo tentar novamente um webhook que falhou?

Uma prática comum é de 3 a 5 retries com backoff exponencial. O número exato depende do seu SLA e da criticidade do evento. Sempre use uma dead-letter queue para eventos que excedam o limite de retries.

Posso usar webhooks sem assinaturas?

Não é recomendado. Sem assinaturas, seu endpoint fica vulnerável a requisições forjadas. Sempre use assinaturas (como HMAC) e valide timestamps para prevenir ataques de replay.

Ao depurar entregas de webhooks, inspecionar os logs do servidor é crucial. Se você usa Nginx, o Nginx Log Analyzer pode ajudar a fazer parse e analisar logs de acesso para rastrear requisições de webhook e identificar falhas.