Webhooks Explicados: Payloads Assinados e Retentativas

Backend2026-10-14TryQuickToolBox

Por que Webhooks São Traiçoeiros

Webhooks são uma ideia simples: em vez de consultar uma API repetidamente para detectar mudanças, o provedor envia eventos para o seu endpoint via HTTP POST. Mas, na prática, webhooks introduzem uma série de desafios que complicam a vida de muitos desenvolvedores: verificar se a requisição realmente veio do provedor, lidar com entregas duplicadas e enfrentar falhas de rede. Se você ignorar isso, corre o risco de brechas de segurança, corrupção de dados e usuários insatisfeitos.

Este artigo aborda os dois aspectos mais críticos de webhooks prontos para produção: payloads assinados e retentativas. Vamos cobrir como verificar assinaturas, como implementar retentativas com backoff exponencial e como tornar seu processamento idempotente.

Payloads Assinados: Provando Autenticidade

Qualquer um pode enviar um HTTP POST para o seu endpoint. Para garantir que a requisição veio do seu provedor, o provedor assina o payload com uma chave secreta e envia a assinatura em um cabeçalho. Sua tarefa é recalcular a assinatura e compará-la.

A maioria dos provedores usa HMAC-SHA256. A assinatura normalmente é calculada sobre o corpo bruto da requisição (às vezes com um timestamp) e codificada em hexadecimal. Por exemplo, o Stripe usa um cabeçalho como Stripe-Signature contendo um timestamp e a assinatura.

Como o HMAC Funciona

HMAC (Hash-based Message Authentication Code) combina uma chave secreta com uma função de hash. Dados a mesma chave e mensagem, a saída é determinística. Sem a chave, um atacante não consegue forjar uma assinatura válida.

Veja um exemplo mínimo em Node.js:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Em Python, é igualmente direto:

import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Boas Práticas para Verificação de Assinatura

Lidando com Timestamps e Ataques de Replay

Muitos provedores incluem um timestamp no cabeçalho da assinatura. Por exemplo, o cabeçalho pode ser t=1614556800,v1=abc123.... Você deve:

  1. Extrair o timestamp e a assinatura.
  2. Verificar se o timestamp está dentro de uma janela aceitável (ex.: 5 minutos).
  3. Recalcular a assinatura usando o timestamp e o payload.
  4. Comparar as assinaturas de forma segura.

Isso impede que um atacante reenvie uma requisição válida antiga.

Retentativas: Lidando com Falhas com Elegância

Webhooks são entregues pela internet pública, então falhas são inevitáveis. Seu endpoint pode estar fora do ar, lento ou retornar um erro 500. Um provedor robusto tentará reenviar entregas falhas, mas você também precisa lidar com retentativas do seu lado se for o provedor.

Backoff Exponencial com Jitter

Ao tentar novamente, evite bombardear o endpoint com retentativas imediatas. Use backoff exponencial: espere mais tempo após cada falha. Adicione jitter (aleatoriedade) para evitar problemas de manada trovejante quando muitos webhooks falham simultaneamente.

Um cronograma típico de retentativas pode ser:

TentativaAtraso (aprox.)
1Imediato
230 segundos
35 minutos
430 minutos
52 horas

Após um número máximo de tentativas, marque a entrega como falha e alerte.

Idempotência: A Chave para Retentativas Seguras

Como retentativas podem causar entregas duplicadas, seu manipulador de webhook deve ser idempotente. Isso significa que processar o mesmo evento várias vezes deve ter o mesmo efeito que processá-lo uma vez.

Como alcançar idempotência:

Implementando Retentativas no Lado do Provedor

Se você está construindo um sistema de webhooks, precisa de um mecanismo de entrega confiável. Aqui está uma abordagem simplificada:

  1. Quando um evento ocorre, enfileire um job de entrega com o payload e a URL de destino.
  2. Um worker pega o job e envia o HTTP POST com um timeout (ex.: 10 segundos).
  3. Se o status da resposta não for 2xx, agende uma retentativa com backoff exponencial.
  4. Acompanhe as tentativas e desista após um máximo (ex.: 5 tentativas).
  5. Registre falhas e forneça uma forma de os usuários reenviarem manualmente.

Use uma fila persistente (ex.: Redis, RabbitMQ ou uma tabela de banco de dados) para que as retentativas sobrevivam a reinicializações.

Juntando Tudo: Um Checklist para Receptor de Webhook

Ao construir um receptor de webhook, siga este checklist:

FAQ

O que é um payload de webhook assinado?

Um payload assinado inclui uma assinatura criptográfica (geralmente HMAC-SHA256) gerada com um segredo compartilhado. O receptor recalcula a assinatura para verificar que a requisição veio do remetente esperado e não foi adulterada.

Por que webhooks precisam de retentativas?

Problemas de rede, indisponibilidade do servidor ou erros temporários podem fazer com que uma entrega de webhook falhe. Retentativas com backoff exponencial garantem que o evento seja eventualmente entregue sem sobrecarregar o receptor.

Como evito processamento duplicado de webhooks?

Use idempotência: inclua um ID de evento único no payload, armazene os IDs processados e projete suas operações para serem idempotentes (ex.: upserts). Assim, mesmo que um webhook seja entregue várias vezes, o efeito é o mesmo.

Se você precisa inspecionar ou depurar payloads de webhook, o JSON Formatter pode ajudar a formatar e validar dados JSON rapidamente.