Elke aflevering van een webhook is ondertekend met het geheim van het adres. Door de handtekening te controleren weet je ontvanger zeker dat het bericht van LeadForge komt en onderweg niet is veranderd. Controleer hem bij elke aflevering, voordat je iets met de gegevens doet.
Headers
Elke aflevering is een POST met deze headers:
Headers
| Naam | Type | Beschrijving |
|---|---|---|
| content-type | tekst | Altijd application/json. |
| user-agent | tekst | Altijd LeadForge-Webhooks/1.0. |
| x-leadforge-event | tekst | De naam van de gebeurtenis, bijvoorbeeld lead.created. |
| x-leadforge-delivery | tekst (uuid) | Het id van de aflevering, gelijk aan id in de body. |
| x-leadforge-signature | tekst | De handtekening: t=<unix-seconden>,v1=<hex>. |
POST /jouw/ontvanger HTTP/1.1
content-type: application/json
user-agent: LeadForge-Webhooks/1.0
x-leadforge-event: lead.created
x-leadforge-delivery: <aflever-id>
x-leadforge-signature: t=1730000000,v1=<64 hex-tekens>
Hoe de handtekening werkt
De header x-leadforge-signature heeft twee delen, gescheiden door een komma:
t: het moment van de poging, in seconden sinds 1 januari 1970 (Unix-tijd).v1: de handtekening, 64 tekens in kleine hexadecimale letters.
LeadForge berekent v1 zo:
v1 = hex( HMAC-SHA256( sleutel = geheim, bericht = t + "." + ruwe body ) )
- De sleutel is het volledige geheim als tekst, inclusief
whsec_aan het begin. - Het bericht is de waarde van
t, een punt en dan de body precies zoals je hem ontvangt.
Je ontvanger rekent hetzelfde uit en vergelijkt de uitkomst met v1. Zijn ze gelijk, dan is de aflevering echt.
Controleren in code
import { createHmac, timingSafeEqual } from 'node:crypto';
// geheim: het volledige geheim van het adres, inclusief whsec_
// ruweBody: de body precies zoals ontvangen, als tekst
// header: de waarde van x-leadforge-signature
export function klopt(geheim, ruweBody, header) {
let t = null;
const handtekeningen = [];
for (const deel of (header ?? '').split(',')) {
const [sleutel, waarde] = deel.trim().split('=');
if (sleutel === 't') t = waarde;
if (sleutel === 'v1' && waarde) handtekeningen.push(waarde);
}
if (!t || !handtekeningen.length) return false;
const verwacht = createHmac('sha256', geheim).update(`${t}.${ruweBody}`, 'utf8').digest('hex');
const b = Buffer.from(verwacht);
return handtekeningen.some((v) => {
const a = Buffer.from(v);
return a.length === b.length && timingSafeEqual(a, b);
});
}
// Voorbeeld met Express: lees de body als tekst, niet als JSON.
// app.post('/leadforge', express.text({ type: 'application/json' }), (req, res) => {
// if (!klopt(geheim, req.body, req.get('x-leadforge-signature'))) return res.sendStatus(401);
// const gebeurtenis = JSON.parse(req.body);
// res.sendStatus(200);
// });
import hashlib
import hmac
def klopt(geheim: str, ruwe_body: bytes, header: str) -> bool:
"""geheim: het volledige geheim inclusief whsec_; ruwe_body: de body zoals ontvangen."""
t = None
handtekeningen = []
for deel in (header or "").split(","):
sleutel, _, waarde = deel.strip().partition("=")
if sleutel == "t":
t = waarde
if sleutel == "v1" and waarde:
handtekeningen.append(waarde)
if not t or not handtekeningen:
return False
bericht = t.encode() + b"." + ruwe_body
verwacht = hmac.new(geheim.encode(), bericht, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(v, verwacht) for v in handtekeningen)
# Voorbeeld met Flask:
# @app.post("/leadforge")
# def leadforge():
# if not klopt(geheim, request.get_data(), request.headers.get("x-leadforge-signature", "")):
# return "", 401
# gebeurtenis = request.get_json()
# return "", 200
Vergelijk de handtekening met een vergelijking die altijd even lang duurt, zoals timingSafeEqual in Node.js of hmac.compare_digest in Python, en niet met === of ==.
Let op
Problemen oplossen
- De handtekening klopt nooit. Controleer of je het volledige geheim gebruikt, inclusief
whsec_, en of je over de ruwe body rekent. - Het geheim is kwijt. Het is niet meer te tonen. Verwijder het adres en voeg het opnieuw toe; zie Het geheim.
- Dezelfde aflevering komt vaker binnen. Een aflevering kan na een fout opnieuw komen, met hetzelfde id en een nieuwe
t. Gebruikx-leadforge-deliveryom dubbele afleveringen te herkennen. Zie Aflevering en herhalen.