Pular para o conteĆŗdo principal

Como validar o webhook

Introdução​

Quando você cria um webhook através do endpoint POST /webhooks, você define um secret (chave secreta) que serÔ usado para assinar todas as requisições enviadas pela API para o seu endpoint.

Esta assinatura garante que as requisições recebidas no seu webhook são autênticas e foram realmente enviadas pela WPP-API, protegendo sua aplicação contra falsificações.

Como funciona a validação​

Todas as requisições de webhook enviadas pela WPP-API incluem um cabeçalho HTTP chamado x-signature que contém a assinatura HMAC-SHA256 do corpo da requisição.

Para validar a autenticidade da requisição, você deve:

  1. Extrair o cabeçalho x-signature da requisição
  2. Calcular a assinatura HMAC-SHA256 do corpo da requisição usando o secret definido na criação do webhook
  3. Comparar a assinatura calculada com o valor do cabeƧalho x-signature
  4. Se as assinaturas coincidirem, a requisição é autêntica

Implementação​

Aqui estão exemplos de como implementar a validação do webhook em diferentes linguagens:

const crypto = require("crypto");

function validateWebhook(request, secret) {
// Extrair a assinatura do cabeƧalho
const signature = request.headers["x-signature"];

if (!signature) {
throw new Error("Header x-signature não encontrado");
}

// Obter o corpo da requisição como string
const body = JSON.stringify(request.body);

// Calcular a assinatura HMAC-SHA256
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");

// Comparar as assinaturas usando comparação segura
const isValid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);

if (!isValid) {
throw new Error("Assinatura invƔlida");
}

return true;
}

// Exemplo de uso com Express
const express = require("express");
const app = express();

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const secret = "seu_secret_aqui"; // O secret definido ao criar o webhook

try {
// Importante: usar req.body como Buffer
const body = req.body.toString("utf8");
const signature = req.headers["x-signature"];

const expectedSignature = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");

if (signature !== expectedSignature) {
return res.status(401).json({ error: "Assinatura invƔlida" });
}

// Assinatura vƔlida, processar o webhook
const payload = JSON.parse(body);
console.log("Webhook vƔlido recebido:", payload);

res.status(200).json({ success: true });
} catch (error) {
console.error("Erro ao validar webhook:", error);
res.status(400).json({ error: error.message });
}
});

Pontos importantes​

1. Use o corpo bruto da requisição​

Ɖ fundamental usar o corpo exato da requisição HTTP para calcular a assinatura. NĆ£o use uma versĆ£o parseada e depois serializada do JSON, pois isso pode alterar a formatação e invalidar a assinatura.

āŒ Incorreto:

const body = JSON.stringify(req.body); // Pode ter formatação diferente

āœ… Correto:

// Use middleware que preserve o corpo bruto
app.use(express.raw({ type: "application/json" }));
const body = req.body.toString("utf8");

2. Comparação segura de strings​

Sempre use funções de comparação de tempo constante para evitar ataques de timing:

  • Node.js: crypto.timingSafeEqual()
  • Python: hmac.compare_digest()
  • PHP: hash_equals()
  • Java: MessageDigest.isEqual()
  • Go: hmac.Equal()
  • C#: CryptographicOperations.FixedTimeEquals()

3. Armazene o secret de forma segura​

Nunca exponha o secret do webhook no código-fonte ou em repositórios públicos. Use variÔveis de ambiente ou serviços de gerenciamento de secrets:

# .env
WEBHOOK_SECRET=seu_secret_super_seguro_aqui

4. Tratamento de erros​

Sempre retorne status HTTP apropriados:

  • 401 Unauthorized: Quando a assinatura for invĆ”lida ou o header estiver ausente
  • 400 Bad Request: Quando o corpo da requisição for invĆ”lido
  • 200 OK: Quando o webhook for processado com sucesso

Testando a validação​

Você pode testar sua implementação gerando manualmente uma assinatura:

# Exemplo usando openssl
echo -n '{"test":"data"}' | openssl dgst -sha256 -hmac "seu_secret_aqui"

Ou usando um script Node.js:

const crypto = require("crypto");

const body = JSON.stringify({ test: "data" });
const secret = "seu_secret_aqui";

const signature = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");

console.log("Signature:", signature);
console.log("Body:", body);
console.log("\nCurl command:");
console.log(`curl -X POST http://localhost:3000/webhook \\
-H "Content-Type: application/json" \\
-H "x-signature: ${signature}" \\
-d '${body}'`);

Próximos passos​

Agora que vocĆŖ sabe como validar webhooks, confira: