Webhooks Explicados: Payloads Assinados e Retentativas

Backend2026-10-22TryQuickToolBox

Você criou um endpoint de webhook para receber eventos de um serviço de terceiros. Tudo funciona nos testes, mas em produção você nota eventos duplicados, notificações perdidas e ocasionais preocupações de segurança com requisições forjadas. Essas são armadilhas comuns ao implementar webhooks sem as devidas proteções.

Webhooks são callbacks HTTP que permitem que serviços notifiquem sua aplicação quando algo acontece. Diferente do polling, eles fornecem atualizações em tempo real. Mas para usá-los de forma confiável, você precisa abordar dois aspectos críticos: autenticidade (garantir que a requisição veio do remetente esperado) e entrega (lidar com falhas e retentativas). Este artigo explica ambos com exemplos práticos de código.

Como os Webhooks Funcionam

Um webhook é um endpoint HTTP POST definido pelo usuário. Quando um evento ocorre, o serviço de origem envia uma requisição para essa URL com um payload descrevendo o evento. Por exemplo, um processador de pagamentos pode enviar um webhook quando uma cobrança é bem-sucedida.

O fluxo é simples:

  1. Você registra uma URL de webhook no serviço.
  2. O serviço envia um HTTP POST para essa URL quando um evento é disparado.
  3. Seu endpoint processa o payload e retorna um status 2xx para confirmar o recebimento.

Sem a verificação adequada, qualquer pessoa pode enviar requisições para seu endpoint. Sem tratamento de retentativas, falhas transitórias podem causar perda de eventos. Vamos abordar cada um.

Protegendo Webhooks com Payloads Assinados

Para garantir que um webhook se originou do remetente esperado, os serviços assinam o payload usando um segredo compartilhado. A assinatura é tipicamente um HMAC (Hash-based Message Authentication Code) do corpo da requisição, enviado em um cabeçalho como X-Signature.

Como a Assinatura HMAC Funciona

O remetente calcula um HMAC usando uma chave secreta e o corpo bruto da requisição, e então inclui a assinatura codificada em hexadecimal em um cabeçalho. Seu endpoint recalcula o HMAC com o mesmo segredo e compara com a assinatura recebida. Se corresponderem, a requisição é autêntica.

Aqui está um exemplo mínimo em Node.js usando Express:

const crypto = require('crypto');
const express = require('express');
const app = express();

// Use raw body for signature verification
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 payload = req.body;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .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(payload);
  // ... handle event
  res.status(200).send('OK');
});

Pontos-chave:

Medidas de Segurança Adicionais

Implementando Retentativas Confiáveis

A entrega de webhooks não é garantida. Redes falham, seu servidor pode estar fora do ar, ou você pode retornar um status não-2xx. Para lidar com isso, os remetentes geralmente tentam novamente com backoff exponencial. Como receptor, você deve projetar seu endpoint para ser idempotente e responder rapidamente.

No Lado do Remetente (Se Você Envia Webhooks)

Se você está construindo um serviço que envia webhooks, implemente retentativas com backoff. Uma estratégia comum:

Exemplo de lógica de retentativa em Python:

import time
import requests
from requests.exceptions import RequestException

def send_webhook(url, payload, max_retries=5):
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=payload, timeout=5)
            if 200 <= response.status_code < 300:
                return True
            elif 400 <= response.status_code < 500 and response.status_code != 429:
                # Client error, don't retry
                return False
        except RequestException:
            pass
        # Exponential backoff with jitter
        delay = (2 ** attempt) + random.uniform(0, 1)
        time.sleep(delay)
    return False

No Lado do Receptor

Seu endpoint deve:

Exemplo de idempotência usando uma restrição única de banco de dados:

CREATE TABLE processed_events (
  event_id VARCHAR(255) PRIMARY KEY,
  processed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Antes de processar, tente inserir o ID do evento. Se violar a chave primária, pule o processamento.

Testando Webhooks Localmente

Durante o desenvolvimento, você precisa de uma URL pública para o serviço alcançar seu servidor local. Ferramentas como ngrok ou localtunnel expõem seu localhost. Sempre use segredos de teste e verifique assinaturas em desenvolvimento para detectar problemas cedo.

Comparação: Polling vs Webhooks

Aspecto Polling Webhooks
Latência Depende do intervalo Quase em tempo real
Carga no servidor Alta (requisições frequentes) Baixa (apenas em eventos)
Complexidade Simples de implementar Requer verificação de assinatura e tratamento de retentativas
Confiabilidade Você controla as retentativas Depende da política de retentativa do remetente

FAQ

O que é um payload de webhook assinado?

Um payload assinado inclui uma assinatura criptográfica (geralmente HMAC) gerada com um segredo compartilhado. Isso permite que o receptor verifique que a requisição veio do remetente esperado e não foi adulterada.

Como lidar com entregas duplicadas de webhook?

Torne seu endpoint idempotente usando um ID de evento único do payload. Armazene IDs processados e ignore duplicatas. Isso garante que retentativas ou problemas de rede não causem processamento duplo.

Devo tentar novamente entregas de webhook com falha?

Sim, mas apenas para erros transitórios (problemas de rede, 5xx). Use backoff exponencial com jitter e defina um limite máximo de retentativas. Evite tentar novamente em erros 4xx (exceto 429), pois indicam um problema com a própria requisição.

Ao depurar payloads de webhook, você frequentemente precisa inspecionar dados JSON. O JSON Formatter pode ajudar você a formatar e validar payloads JSON rapidamente.