> ## Documentation Index
> Fetch the complete documentation index at: https://docs.exoid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Firma e sicurezza

> Verifica la firma HMAC-SHA256 dell'header X-Signature per accettare solo i payload realmente inviati da Exoid.

Il tuo endpoint webhook è pubblico: chiunque conosca l'URL può inviarti richieste contraffatte. Exoid firma ogni richiesta con HMAC-SHA256 e tu devi verificare la firma prima di fidarti del contenuto.

## Come Exoid firma le richieste

* Exoid firma il **corpo della richiesta non parsato** (il raw body, byte per byte) con HMAC-SHA256, usando il Webhook Secret della campagna.
* La firma viaggia nell'header `X-Signature` nel formato `sha256=<hex>`.
* Il tuo endpoint deve ricreare la firma e confrontarla con quella ricevuta. Se non corrisponde, rifiuta la richiesta.

## Procedura di verifica

<Steps>
  <Step title="Leggi il raw body">
    Ricevi il corpo come Buffer non parsato, senza applicare alcun middleware JSON.
  </Step>

  <Step title="Estrai l'header">
    Prendi il valore di `X-Signature` dalla richiesta in ingresso.
  </Step>

  <Step title="Ricalcola l'HMAC">
    Calcola `HMAC-SHA256` del raw body con il Webhook Secret della campagna e formatta il risultato come `sha256=<hex>`.
  </Step>

  <Step title="Confronta">
    Se la firma calcolata non coincide con quella ricevuta, rispondi `401` e interrompi l'elaborazione.
  </Step>

  <Step title="Elabora il payload">
    Solo a questo punto esegui `JSON.parse` sul corpo e usa i dati.
  </Step>
</Steps>

## Esempio in Node.js (Express)

```js title="verifica-firma.js" theme={"dark"}
const crypto = require("crypto");
const express = require("express");
const app = express();

// Usa express.raw per accedere al corpo non parsato e verificare la firma
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-signature"];
  const expected =
    "sha256=" +
    crypto
      .createHmac("sha256", process.env.WEBHOOK_SECRET)
      .update(req.body) // raw Buffer
      .digest("hex");

  if (sig !== expected) return res.status(401).send("Firma non valida");

  const payload = JSON.parse(req.body);
  console.log("Webhook ricevuto:", payload);

  res.sendStatus(200);
});

app.listen(3000, () => console.log("Listener webhook attivo su :3000"));
```

<Warning>
  Devi calcolare l'HMAC sul corpo **non parsato**. Se applichi `JSON.parse` (o un middleware come `express.json()`) prima della verifica e poi ri-serializzi l'oggetto, i byte cambiano — spaziatura, ordine delle chiavi, escaping — e il confronto fallisce anche su richieste perfettamente legittime. Usa sempre il raw Buffer, ad esempio con `express.raw`.
</Warning>

<Tip>
  Confronta le firme in tempo costante (ad esempio con `crypto.timingSafeEqual`) per non esporre informazioni tramite il tempo di risposta. E non registrare mai il Webhook Secret nei log applicativi.
</Tip>

<Note>
  Un endpoint che risponde `401` non riceve ritentativi: `401` è un errore permanente. Assicurati che la verifica sia corretta prima di andare in produzione, altrimenti perdi gli eventi.
</Note>

## Prossimi passi

<CardGroup cols={2}>
  <Card title="Struttura del payload" icon="file-json" href="/it/webhooks/payload">
    Il modello dati TypeScript, i tipi di risposta e un payload di esempio.
  </Card>

  <Card title="Configurazione" icon="settings" href="/it/webhooks/configurazione">
    Dove imposti il secret e come lanci una richiesta di test.
  </Card>
</CardGroup>


## Related topics

- [Configurazione](/it/webhooks/configurazione.md)
- [Come funzionano](/it/webhooks/introduzione.md)
- [Risoluzione dei problemi](/it/domande-frequenti.md)
