Verificação de Assinatura
Todos os payloads de webhook são assinados com HMAC-SHA256 para que você possa verificar que a requisição veio do SimpleCRM e não foi alterada.
1. Obtenha o Secret
Ao criar um webhook na interface web do SimpleCRM, um secret é gerado automaticamente (formato: whsec_...).
Importante: O secret é exibido apenas uma vez, no momento da criação. Copie e armazene-o em local seguro (ex: variável de ambiente no seu servidor).
Você pode encontrar o secret na tela de Configurações → Webhooks da aplicação web. Se perdeu o secret, delete o webhook e crie um novo.
2. Como Assinamos
Concatenamos o timestamp e o body cru da requisição, separados por um ponto, e geramos um HMAC-SHA256 usando seu secret:
message = "{timestamp}.{raw_body}"
signature = HMAC-SHA256(secret, message)
header = "sha256={signature}"O resultado é enviado no header X-Webhook-Signature.
3. Exemplo de Verificação
Node.js
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, timestamp, secret) {
const message = timestamp + '.' + rawBody;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
const expected = 'sha256=' + expectedSignature;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// No seu endpoint de webhook:
app.post('/webhooks/simplecrm', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const rawBody = JSON.stringify(req.body);
if (!verifyWebhookSignature(rawBody, signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Assinatura válida - processar evento
const { event, data } = req.body;
console.log('Evento recebido:', event, data);
res.status(200).json({ received: true });
});Python
import hmac
import hashlib
def verify_webhook_signature(raw_body, signature, timestamp, secret):
message = f"{timestamp}.{raw_body}"
expected = "sha256=" + hmac.new(
secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Segurança: Sempre use comparação em tempo constante (como timingSafeEqual ou compare_digest) para evitar ataques de timing. Valide também o timestamp para rejeitar eventos antigos (recomendado: rejeitar se > 5 minutos).