# Perché la firma del webhook non corrisponde?

> Risolvi gli errori di verifica della firma dei webhook. Firma il corpo grezzo con il secret whsec_ completo e il valore di X-Emailit-Timestamp.

Questo articolo ti aiuta quando il tuo endpoint rifiuta i webhook di Emailit perché la firma che calcoli non è uguale all’header `X-Emailit-Signature`. In quasi tutti i casi i byte che firmi o il secret che usi sono diversi da quelli usati da Emailit.

## Sintomi

- Il gestore restituisce `401` o `400` con «invalid signature» per ogni webhook, compresi gli eventi **Send test** inviati dal pannello.
- Le richieste nella scheda **Requests** del webhook mostrano tentativi non riusciti con il codice di stato di errore del tuo endpoint.
- La verifica funziona in un test unitario con un payload creato a mano, ma non con le consegne reali.

## Causa

Emailit firma ogni richiesta così:

```text
signature = hex( HMAC-SHA256( secret, "<X-Emailit-Timestamp>.<raw request body>" ) )
```

La firma non corrisponde quando uno dei tre input è diverso:

- **Il corpo è stato serializzato di nuovo.** Spesso i framework analizzano il JSON prima che venga eseguito il gestore. Chiamare `JSON.stringify(req.body)` produce spazi e ordine delle chiavi diversi dai byte inviati da Emailit. Il corpo è un **array** JSON di massimo 100 eventi, quindi anche il codice che si aspetta un oggetto potrebbe modificarne la struttura.
- **Il secret è sbagliato.** Usa il valore completo, compreso il prefisso `whsec_`. Ogni webhook ha il proprio secret, e reimpostarlo invalida subito quello vecchio, anche per i nuovi tentativi in sospeso.
- **Il timestamp manca o è diverso.** Usa il valore esatto dell’header `X-Emailit-Timestamp` e uniscilo al corpo con un solo punto.
- **Il confronto è sbagliato.** L’header è in esadecimale minuscolo, non in Base64, e non ha il prefisso `sha256=`.
- **Qualcosa davanti alla tua app ha modificato il corpo**, ad esempio un proxy che lo ricodifica o lo decomprime.

## Soluzione

1. **Leggi il corpo grezzo.** Cattura i byte prima che venga eseguito qualsiasi parser JSON. In Express, monta `express.raw()` solo sulla rotta del webhook:

```javascript title="webhook.js"
import crypto from 'node:crypto';

app.post('/webhooks/emailit', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Emailit-Timestamp');
  const expected = crypto
    .createHmac('sha256', process.env.EMAILIT_WEBHOOK_SECRET) // whsec_...
    .update(`${timestamp}.${req.body.toString('utf8')}`)
    .digest('hex');
  const received = req.get('X-Emailit-Signature') ?? '';
  const valid = received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  if (!valid) return res.sendStatus(401);

  const events = JSON.parse(req.body); // an array of events
  res.sendStatus(200);
});
```

   Nei route handler di Next.js, usa `await request.text()`. In Laravel, usa `$request->getContent()`. In Flask, usa `request.get_data()`.

2. **Recupera il secret corretto.** Chiama [Recupera un webhook](/it/docs/api-reference/webhooks/get/), che restituisce il `secret` attuale. Nel pannello il secret viene mostrato una sola volta, quindi se l’hai perso apri il webhook, scegli **Webhook secret** per reimpostarlo e aggiorna subito la variabile d’ambiente.

3. **Invia un evento di test.** Nella pagina del webhook, scegli **Send test** e seleziona un tipo di evento. Il test è firmato con lo stesso secret, e la finestra mostra il codice di stato e il corpo della risposta del tuo endpoint.

4. **Controlla la tolleranza sull’orario.** Se rifiuti i timestamp vecchi per evitare attacchi di replay, concedi qualche minuto di margine e mantieni sincronizzato l’orologio del server. Ogni tentativo di consegna, compresi i nuovi tentativi, ha un timestamp nuovo.

5. **Ritenta ciò che non è riuscito.** Quando la verifica funziona, scegli **Retry failed** per inviare di nuovo le consegne non riuscite degli ultimi 7 giorni.

Per il codice in altri linguaggi, vedi [Verifica le firme dei webhook](/it/docs/webhooks/request-signature/). Per il formato del corpo, vedi [Richieste webhook](/it/docs/webhooks/webhook-requests/).

## Serve ancora aiuto?

[Contatta il supporto](/contact/) o chiedi su [Discord](https://discord.emailit.com). Indica l’ID del webhook (`wh_…`) e l’ID di un evento non riuscito, mai il secret.

---
Fonte: https://emailit.com/it/docs/kb/webhook-signature-mismatch/
