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 di consegna
Sezione intitolata “Header di consegna”| 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 |
Come si calcola la firma
Sezione intitolata “Come si calcola la firma”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).
- 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.
- Calcola l’HMAC-SHA256 del corpo grezzo con il tuo signing secret.
- Confronta il risultato con
X-AiTrack-Signature(tolto il prefissosha256=) usando un confronto a tempo costante. - Se non combacia, rispondi
401e 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(); },);import hashlibimport hmacimport os
def verify_aitrack_signature(raw_body: bytes, signature_header: str, secret: str) -> bool: expected = "sha256=" + hmac.new( secret.encode("utf-8"), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature_header or "", expected)
# Esempio con Flask: request.get_data() dà il corpo grezzo prima del parsing.@app.route("/webhooks/aitrack", methods=["POST"])def aitrack_webhook(): raw_body = request.get_data() signature = request.headers.get("X-AiTrack-Signature", "") secret = os.environ["AITRACK_WEBHOOK_SECRET"]
if not verify_aitrack_signature(raw_body, signature, secret): return "", 401
event = request.get_json() # idempotenza: scarta se hai già visto event["id"] return "", 200# Utile per un test rapido da terminale con un payload salvato su file:# ricalcola l'HMAC e confrontalo a occhio con l'header X-AiTrack-Signature# ricevuto (va bene solo per debug manuale, non per un endpoint in produzione).openssl dgst -sha256 -hmac "$AITRACK_WEBHOOK_SECRET" payload.jsonIdempotenza
Sezione intitolata “Idempotenza”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.
Testare un endpoint
Sezione intitolata “Testare un endpoint”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.