KivoSign

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çalhoDescrição
X-Documenso-SecretO 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çãoDescrição
Retorne status 401Responda 401 Unauthorized para indicar falha de autenticação
Registre a tentativaGuarde as tentativas com falha para monitoramento de segurança
Não processeNunca processe o payload se a verificação falhar
Não exponha detalhesNã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

ProblemaCausaSolução
Segredo diferenteO segredo mudou ou foi configurado erradoConfira se o segredo do seu ambiente é igual ao da KivoSign
Cabeçalho vazioWebhook criado sem segredoDefina um segredo na configuração do webhook na KivoSign
Problema de codificaçãoCodificações de texto diferentesUse a mesma codificação (UTF-8) nos dois lados

Boas práticas de segurança

Veja também

Nesta página