Aller au contenu

Firma e sicurezza

Ce contenu n’est pas encore disponible dans votre langue.

Ogni consegna è firmata. Verificala sempre: un endpoint che accetta qualunque POST è una porta aperta per chiunque scopra il tuo indirizzo.

Header Valore
Content-Type application/json
X-AiTrack-Signature sha256=<HMAC-SHA256 esadecimale del corpo, con il signing secret dell'endpoint>
X-AiTrack-Signature-Version versione dello schema di firma dell’endpoint (default v1)
X-AiTrack-Timestamp epoch in millisecondi del tentativo di consegna
X-AiTrack-Delivery-Id id univoco di questa consegna
X-AiTrack-Delivery-Attempt numero del tentativo (1, 2, 3…)
Idempotency-Key <id endpoint>:<id evento>, stabile tra i retry dello stesso evento

Il server calcola HMAC_SHA256(signing_secret, JSON.stringify(payload)) e la invia come sha256=<hex> nell’header X-AiTrack-Signature. Per verificarla devi ricalcolare lo stesso HMAC sul corpo grezzo della richiesta (prima di qualunque JSON.parse) con il signing secret del tuo endpoint, e confrontarlo in modo a tempo costante (mai con === su stringhe: un confronto normale può far trapelare la firma un byte alla volta tramite timing attack).

  1. Leggi il corpo della richiesta come bytes/stringa grezza, non come oggetto già deserializzato — molti framework fanno il parsing JSON prima che tu possa intercettare il corpo originale: configura il tuo middleware per darti accesso al raw body su questa rotta.
  2. Calcola l’HMAC-SHA256 del corpo grezzo con il tuo signing secret.
  3. Confronta il risultato con X-AiTrack-Signature (tolto il prefisso sha256=) usando un confronto a tempo costante.
  4. Se non combacia, rispondi 401 e scarta la richiesta — non elaborarla.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyAitrackSignature(rawBody, signatureHeader, secret) {
const atteso = 'sha256=' + createHmac('sha256', secret)
.update(rawBody) // il corpo GREZZO, prima di JSON.parse
.digest('hex');
const ricevuto = signatureHeader || '';
const ok = ricevuto.length === atteso.length
&& timingSafeEqual(Buffer.from(ricevuto), Buffer.from(atteso));
return ok;
}
// Esempio con Express: serve il raw body su questa rotta specifica.
app.post(
'/webhooks/aitrack',
express.raw({ type: 'application/json' }),
(req, res) => {
const ok = verifyAitrackSignature(
req.body, // Buffer, grazie a express.raw()
req.headers['x-aitrack-signature'],
process.env.AITRACK_WEBHOOK_SECRET,
);
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body);
// idempotenza: scarta se hai già visto event.id
res.status(200).end();
},
);

Idempotency-Key (e l’id dentro il payload) sono stabili per evento: se ti arriva due volte lo stesso id — per un retry dopo un timeout momentaneo sul tuo lato — scartalo invece di rielaborarlo. Vedi Consegna e retry per la politica completa.

Usa POST /api/webhooks/:id/test: invia un evento sintetico type: "ping" solo all’endpoint indicato, firmato come una consegna reale — così puoi verificare la tua implementazione senza aspettare un evento vero.