Assinatura e verificação
Verifique o header X-FonteData-Signature (HMAC-SHA256) antes de confiar no conteúdo, com exemplos em Python e Node.
Toda requisição de webhook enviada pela FonteData carrega um cabeçalho de assinatura que permite confirmar que o conteúdo realmente veio da FonteData e não foi alterado no caminho.
O cabeçalho
X-FonteData-Signature: sha256=<hex>
O valor é o HMAC-SHA256 calculado sobre o corpo bruto (raw body) da
requisição, usando como chave o segredo de assinatura gerado quando você cadastrou o
webhook (veja Visão geral). O resultado é codificado em
hexadecimal e prefixado com sha256=.
Por que verificar
Sem verificar a assinatura, qualquer requisição POST para o seu endpoint —
inclusive de terceiros mal-intencionados que descobrirem a URL — seria tratada
como um evento legítimo. A verificação garante que só eventos assinados com o
seu segredo de assinatura sejam processados.
Use sempre uma comparação em tempo constante (hmac.compare_digest em
Python, crypto.timingSafeEqual em Node) em vez de ==/===, para não expor
o segredo a um ataque de timing.
Verificação em Python
import hashlib
import hmac
def verificar_assinatura(corpo_bruto: bytes, header_assinatura: str, secret: str) -> bool:
esperado = "sha256=" + hmac.new(
secret.encode("utf-8"),
corpo_bruto,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(esperado, header_assinatura)
# Exemplo de uso em um handler Flask/FastAPI
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
WEBHOOK_SECRET = "seu_segredo_de_assinatura"
@app.post("/webhooks/fontedata")
async def receber_webhook(request: Request):
corpo_bruto = await request.body()
assinatura = request.headers.get("X-FonteData-Signature", "")
if not verificar_assinatura(corpo_bruto, assinatura, WEBHOOK_SECRET):
raise HTTPException(status_code=401, detail="Assinatura inválida")
evento = await request.json()
# processar evento...
return {"ok": True}
Verificação em Node.js
const crypto = require("crypto");
function verificarAssinatura(corpoBruto, headerAssinatura, secret) {
const esperado =
"sha256=" +
crypto.createHmac("sha256", secret).update(corpoBruto).digest("hex");
const bufEsperado = Buffer.from(esperado);
const bufRecebido = Buffer.from(headerAssinatura || "");
if (bufEsperado.length !== bufRecebido.length) {
return false;
}
return crypto.timingSafeEqual(bufEsperado, bufRecebido);
}
// Exemplo de uso em um handler Express
const express = require("express");
const app = express();
const WEBHOOK_SECRET = "seu_segredo_de_assinatura";
app.post(
"/webhooks/fontedata",
express.raw({ type: "application/json" }),
(req, res) => {
const assinatura = req.headers["x-fontedata-signature"] || "";
if (!verificarAssinatura(req.body, assinatura, WEBHOOK_SECRET)) {
return res.status(401).json({ erro: "Assinatura inválida" });
}
const evento = JSON.parse(req.body);
// processar evento...
return res.status(200).json({ ok: true });
}
);
Rotação do segredo
Se o segredo de assinatura vazar, ou você simplesmente quiser trocá-lo, rotacione pelo painel (Webhooks → seu webhook → rotacionar segredo). Um novo segredo é gerado e exibido uma única vez, exatamente como na criação; o anterior deixa de valer a partir da rotação.