Tradotta dall'inglese. In caso di differenze fa fede la pagina in inglese. Leggi in inglese
Verifica le firme
Dimostra che un webhook arriva davvero da Nembol, con verificatori pronti da copiare in quattro linguaggi.
Ogni consegna contiene:
X-Nembol-Signature: t=1784900412,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdt: timestamp Unix del tentativo di consegna.v1: HMAC-SHA256, calcolato con il signing secret del tuo endpoint sulla stringa"<t>.<raw_body>". Durante una finestra di rotazione del secret potresti vedere due vociv1=. Accetta se almeno una corrisponde.
Passi di verifica: (1) leggi il body grezzo della richiesta, prima di qualsiasi parsing
JSON o trasformazione da parte di un middleware; (2) calcola l'HMAC atteso;
(3) confronta in tempo costante; (4) rifiuta se |now − t| supera i 5
minuti (protezione dai replay).
import crypto from 'node:crypto';
// Express: app.post('/webhook', express.raw({ type: 'application/json' }), handler)
export function verifyNembolSignature(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((kv) => kv.split('=', 2)),
);
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${rawBody}`)
.digest('hex');
return signatureHeader
.split(',')
.filter((kv) => kv.startsWith('v1='))
.some((kv) => {
const candidate = Buffer.from(kv.slice(3), 'hex');
const exp = Buffer.from(expected, 'hex');
return candidate.length === exp.length && crypto.timingSafeEqual(candidate, exp);
});
}L'errore classico: verificare su un body JSON riserializzato. Il JSON che il tuo framework analizza e poi riconverte in stringa non sarà identico byte per byte a quello che Nembol ha inviato, e la firma non corrisponderà. Conserva sempre i byte grezzi.
Ultimo aggiornamento il 20 ottobre 2026