Skip to main content

Segurança dos Webhooks

Visão Geral

Todos os webhooks que a 3X Pay envia para a URL de notificação configurada na sua conta são assinados criptograficamente com HMAC-SHA256. A assinatura permite que você confirme, do seu lado, que a requisição:
  1. Foi realmente enviada pela 3X Pay (autenticidade)
  2. Não foi alterada no caminho (integridade)
  3. Não é uma reentrega maliciosa de uma notificação antiga (proteção contra replay)
A URL de webhook é pública: qualquer pessoa que a descubra pode enviar requisições forjadas para ela. Sempre valide a assinatura antes de processar o payload — nunca confie no corpo de um webhook sem verificar o X-Webhook-Signature.

Chave de assinatura

O segredo usado para gerar a assinatura é o seu api_secret — a mesma credencial usada para autenticar as chamadas à API. Você a encontra no painel da 3X Pay em Integrações > Credenciais de API (veja Credenciais de API).
Trate o api_secret como uma senha. Ele assina os seus webhooks e autentica a sua API. Em caso de vazamento, revogue e gere novas credenciais imediatamente no painel.

Headers de segurança

Toda requisição de webhook inclui os seguintes headers:

Como a assinatura é gerada

A 3X Pay monta uma string chamada signed_payload e calcula o HMAC-SHA256 dela usando o seu api_secret como chave:
O valor enviado em X-Webhook-Signature é sha256= + essa assinatura em hexadecimal.
Use o corpo bruto (raw body) da requisição. A assinatura é calculada sobre os bytes exatos do JSON enviado. Se você desserializar o JSON e serializá-lo novamente antes de calcular o HMAC, a ordem das chaves e os espaços podem mudar e a assinatura não vai bater. Capture o corpo cru antes de qualquer parse.

Como validar (passo a passo)

1

Capture o corpo bruto

Leia o corpo da requisição como string/bytes antes de fazer o parse do JSON.
2

Verifique o timestamp (anti-replay)

Rejeite a requisição se X-Webhook-Timestamp estiver fora de uma janela de tolerância (recomendado: ±5 minutos / 300 segundos) em relação ao horário atual.
3

Recalcule o HMAC

Monte signed_payload = timestamp + "." + corpo_bruto e calcule HMAC_SHA256(signed_payload, api_secret) em hexadecimal.
4

Compare em tempo constante

Compare o valor calculado com o recebido em X-Webhook-Signature (sem o prefixo sha256=) usando uma função de comparação em tempo constante (timingSafeEqual, hmac.compare_digest, hash_equals). Nunca use == simples.
5

Garanta idempotência

Use o X-Webhook-Event-Id para descartar entregas duplicadas (retentativas reenviam o mesmo Event-Id). Só então processe o payload.

Exemplos de implementação

Idempotência e retentativas

  • Se a sua aplicação não responder com HTTP 2xx, a 3X Pay reenvia o webhook automaticamente: até 5 tentativas, com backoff exponencial (intervalo inicial de ~5 segundos).
  • Todas as tentativas do mesmo evento carregam o mesmo X-Webhook-Event-Id. Persista esse identificador e ignore eventos já processados para evitar processamento duplicado (ex.: creditar o mesmo pagamento duas vezes).
  • A cada tentativa o X-Webhook-Timestamp é renovado e a assinatura é recalculada. Por isso, valide a assinatura sempre com o X-Webhook-Timestamp da própria requisição recebida, não com um valor armazenado.

Rotação do api_secret

Ao gerar novas credenciais no painel, a assinatura dos webhooks pode continuar usando o segredo anterior por um curto período (até ~1 hora, devido a cache interno). Durante uma rotação planejada, aceite temporariamente assinaturas válidas com o segredo antigo ou com o novo até confirmar que apenas o novo está em uso.

Boas práticas de segurança