Verificação de webhooks
Verifique a autenticidade dos webhooks recebidos.
Visão geral
Verificar as requisições de webhook garante que o payload veio da KivoSign e não foi adulterado. Sem verificação, alguém poderia forjar requisições para o seu endpoint e disparar ações indevidas.
Como a KivoSign identifica os webhooks
Quando você configura um webhook com um segredo, a KivoSign envia esse segredo em toda requisição, no cabeçalho X-Documenso-Secret. Seu servidor deve comparar esse valor com o segredo guardado para autenticar a requisição.
O nome do cabeçalho é X-Documenso-Secret por compatibilidade com a plataforma em que a KivoSign
se baseia. Use exatamente esse nome no seu código.
POST /seu-endpoint-de-webhook HTTP/1.1
Host: seu-servidor.com
Content-Type: application/json
X-Documenso-Secret: seu_segredo_de_webhook
{"event": "DOCUMENT_COMPLETED", "payload": {...}}Formato do cabeçalho
| Cabeçalho | Descrição |
|---|---|
X-Documenso-Secret | O segredo que você definiu ao criar o webhook |
O cabeçalho traz o segredo como texto puro. Se você não configurou um segredo, o cabeçalho vem vazio.
Passos da verificação
Leia o cabeçalho X-Documenso-Secret da requisição recebida
Compare com o segredo guardado usando uma comparação de tempo constante
Rejeite a requisição se os valores forem diferentes
Processe o payload se a verificação passar
Use sempre comparação de strings em tempo constante, para evitar ataques de temporização. Os
operadores comuns (=== ou ==) podem vazar informação sobre o segredo pela variação do tempo de resposta.
Exemplos
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.KIVOSIGN_WEBHOOK_SECRET;
function verifyWebhookSignature(receivedSecret, expectedSecret) {
if (!expectedSecret) {
// Sem segredo configurado, a verificação é ignorada
// Não recomendado em produção
return true;
}
if (!receivedSecret) {
return false;
}
// Comparação em tempo constante para evitar ataques de temporização
try {
return crypto.timingSafeEqual(
Buffer.from(receivedSecret),
Buffer.from(expectedSecret),
);
} catch {
return false;
}
}
app.post('/webhooks/kivosign', (req, res) => {
const receivedSecret = req.headers['x-documenso-secret'];
if (!verifyWebhookSignature(receivedSecret, WEBHOOK_SECRET)) {
console.error('Falha na verificação do webhook');
return res.status(401).json({ error: 'Invalid signature' });
}
const { event, payload } = req.body;
console.log(`Webhook verificado: ${event}`);
res.status(200).json({ received: true });
});
app.listen(3000, () => {
console.log('Servidor de webhooks na porta 3000');
});Quando a verificação falha
Siga estas práticas:
| Ação | Descrição |
|---|---|
| Retorne status 401 | Responda 401 Unauthorized para indicar falha de autenticação |
| Registre a tentativa | Guarde as tentativas com falha para monitoramento de segurança |
| Não processe | Nunca processe o payload se a verificação falhar |
| Não exponha detalhes | Não revele por que a verificação falhou |
app.post('/webhooks/kivosign', (req, res) => {
const receivedSecret = req.headers['x-documenso-secret'];
if (!verifyWebhookSignature(receivedSecret, WEBHOOK_SECRET)) {
// Registra para monitoramento, sem expor detalhes
console.error('Falha na verificação do webhook', {
timestamp: new Date().toISOString(),
ip: req.ip,
});
return res.status(401).json({ error: 'Unauthorized' });
}
// Continua o processamento...
});Problemas comuns
| Problema | Causa | Solução |
|---|---|---|
| Segredo diferente | O segredo mudou ou foi configurado errado | Confira se o segredo do seu ambiente é igual ao da KivoSign |
| Cabeçalho vazio | Webhook criado sem segredo | Defina um segredo na configuração do webhook na KivoSign |
| Problema de codificação | Codificações de texto diferentes | Use a mesma codificação (UTF-8) nos dois lados |
Boas práticas de segurança
Veja também
- Configuração de webhooks - Configure endpoints e segredos
- Eventos de webhook - Tipos de evento e estrutura do payload

