# Verifica della firma dei webhook: HMAC-SHA256 e protezione dal replay

> Un webhook viene autenticato tramite una firma HMAC-SHA256 che il mittente calcola su un timestamp più l'esatto raw body, usando un segreto condiviso, e invia in un header. Il ricevitore ricalcola l'HMAC, lo confronta a tempo costante e rifiuta la richiesta se la firma non corrisponde o se il timestamp è fuori da una finestra di tolleranza (protezione dal replay).

- URL canonico: https://www.broadnet.me/it/reference/webhook-signature-verification
- Lingua: it
- Ultimo aggiornamento: 2026-07-19
- Editore: BroadNet (https://www.broadnet.me)

Come verificare che un webhook sia autentico e non manomesso: HMAC-SHA256 sul raw body, gli header di firma e timestamp, la protezione dal replay con una tolleranza di timestamp e un nonce, il confronto a tempo costante per sconfiggere gli attacchi di timing, e la rotazione sicura del segreto con chiavi sovrapposte.

## Perché le firme, non le allowlist di IP

Un endpoint di webhook è un URL HTTPS pubblico, quindi chiunque può eseguirvi un POST. TLS dimostra che il client ha raggiunto il tuo server; non dimostra affatto che la richiesta provenga effettivamente dalla piattaforma di messaggistica. L'allowlisting di IP è fragile (gli IP dei mittenti cambiano e fallisce dietro un egress condiviso). La risposta standard è un message authentication code con chiave: mittente e ricevitore condividono un segreto, il mittente firma ogni payload con esso e il ricevitore verifica. HMAC (RFC 2104) con SHA-256 (RFC 6234) è la costruzione onnipresente perché è veloce, standardizzata e — a differenza di un semplice hash del segreto concatenato al messaggio — dimostrabilmente resistente agli attacchi di length-extension. Una firma valida dimostra due cose contemporaneamente: il payload è stato prodotto da un detentore del segreto e non è stato alterato in transito.

## Gli header di firma e timestamp

Il mittente include almeno due header: un timestamp (quando la richiesta è stata firmata) e una firma. Il contenuto firmato non è il solo JSON, ma una stringa canonica che lega il timestamp al body — tipicamente il timestamp, un separatore e il raw body della richiesta — così che un attaccante non possa trasferire una firma valida su un payload diverso né riprodurla in un momento diverso. La denominazione varia da vendor a vendor (Webhook-Signature / Webhook-Timestamp, oppure X-Signature / X-Timestamp), ma la costruzione è la stessa. La firma è di solito hex o base64 e spesso porta un prefisso di schema (per esempio v1,) così che il segreto possa essere ruotato a un nuovo schema senza rompere i vecchi verificatori.

*Contenuto firmato e layout degli header*

```text
signed_content = timestamp + '.' + raw_body
signature      = HMAC_SHA256(secret, signed_content)

Webhook-Timestamp: 1752916522
Webhook-Signature: v1,3f2b8c9a1d...   # hex or base64, scheme-prefixed
```

## Verificare sul raw body

Il bug di verifica più comune in assoluto è firmare un body ri-serializzato. L'HMAC è calcolato su byte esatti, quindi il ricevitore deve verificare sul raw body della richiesta esattamente come ricevuto — prima di qualsiasi parse-and-re-encode JSON. Fare il parsing verso un oggetto e ri-stringificarlo riordina le chiavi, cambia gli spazi e normalizza numeri ed escape Unicode, producendo byte diversi e una firma non corrispondente anche quando nulla è stato manomesso. In pratica ciò significa catturare il raw body nel web framework prima che il middleware di body-parsing lo consumi, verificare l'HMAC su quei byte, e solo allora fare il parsing del JSON per l'uso. Anche il charset conta: firma e verifica la stessa sequenza di ottetti (UTF-8), non una stringa decodificata.

*Verifica (pseudocodice) — raw body, tempo costante*

```text
raw   = request.raw_body            # exact bytes, pre-parse
ts    = header('Webhook-Timestamp')
sig   = header('Webhook-Signature')

if abs(now() - ts) > TOLERANCE: reject   # replay window
expected = hmac_sha256(secret, ts + '.' + raw)
if not constant_time_equal(expected, sig): reject
accept(parse_json(raw))
```

## Protezione dal replay: tolleranza di timestamp e nonce

Una firma da sola non ferma il replay — una richiesta catturata ma valida potrebbe essere reinviata più tardi. Due meccanismi colmano questa lacuna. Primo, poiché il timestamp è all'interno del contenuto firmato, il ricevitore lo confronta con il proprio orologio e rifiuta qualsiasi cosa più vecchia di una tolleranza (comunemente pochi minuti, abbastanza ampia da assorbire lo skew di orologio e il ritardo dei retry, abbastanza stretta da limitare la finestra di replay). Secondo, per garanzie più stringenti il ricevitore registra un nonce — l'event id univoco — per il periodo di tolleranza e rifiuta una seconda richiesta che porta un id già visto. La tolleranza di timestamp delimita la finestra; la cache dei nonce rende inefficace il replay all'interno di quella finestra. Nota che ciò è complementare all'idempotenza del consumer, che gestisce i duplicati legittimi anziché i replay malevoli.

*Controlli di protezione dal replay*

| Controllo | Difende da | Tuning |
|---|---|---|
| Timestamp nel contenuto firmato | Spostare una firma su un nuovo istante temporale | Deve essere firmato, non una semplice intestazione |
| Finestra di tolleranza del timestamp | Riproduzione ritardata di una richiesta acquisita | Pochi minuti; compromesso tra sfasamento dell'orologio ed esposizione |
| Cache Nonce / ID evento | Replay entro la finestra di tolleranza | Memorizza gli ID in cache almeno per la durata della tolleranza |
| Confronto a tempo costante | Side-channel temporale sulla firma | Sempre, a prescindere da quanto sopra |

## Confronto a tempo costante

Confrontare le firme calcolata e ricevuta con un normale controllo di uguaglianza rivela informazioni: un confronto byte per byte termina prima in caso di mismatch precoce, e un attaccante in grado di misurare quel timing può recuperare la firma corretta un byte alla volta. La verifica deve quindi usare un confronto a tempo costante (indipendente dalla lunghezza) — la funzione di confronto timing-safe della standard library di ogni piattaforma — che esamina sempre l'intera lunghezza prima di restituire. È economico e non negoziabile; è la differenza tra uno schema di firma solido e uno sconfiggibile con un numero sufficiente di richieste temporizzate.

## Rotazione del segreto

I segreti di firma devono poter essere ruotati senza far cadere webhook legittimi a metà volo. Il pattern sicuro è quello delle chiavi sovrapposte: al mittente viene fornito un nuovo segreto mentre quello vecchio resta valido e, durante la sovrapposizione, il mittente o firma con entrambi (emettendo due firme) oppure il ricevitore accetta una firma che corrisponde a uno dei due segreti. Una volta confermato che ogni producer è passato al nuovo segreto, quello vecchio viene ritirato. Il prefisso di schema sulla firma (v1, v2,) rende questo osservabile e consente di far viaggiare insieme una modifica di algoritmo versionata. Il validatore di firma dei webhook ospitato da BroadNet ti permette di verificare un payload, un timestamp e un segreto rispetto a un HMAC atteso senza scrivere codice; i meccanismi della firma DLR con HMAC-SHA256 sono trattati nella libreria delle risposte.

## Domande frequenti

### Come si calcola una firma di webhook?

Il mittente costruisce una stringa canonica che lega il timestamp al raw body (tipicamente timestamp + '.' + body), calcola HMAC-SHA256 su di essa con un segreto condiviso e inserisce il risultato in un header di firma insieme al timestamp. Il ricevitore ricalcola lo stesso HMAC e confronta. Una corrispondenza dimostra che il payload proviene da un detentore del segreto e non è stato alterato.

### Perché devo verificare sul raw body della richiesta?

L'HMAC è calcolato su byte esatti. Se fai il parsing del JSON e lo ri-serializzi, l'ordine delle chiavi, gli spazi, la formattazione dei numeri e gli escape Unicode cambiano, quindi i byte differiscono e la firma non corrisponde anche quando nulla è stato manomesso. Cattura il raw body prima del middleware di body-parsing, verifica, poi fai il parsing.

### Come ferma la tolleranza di timestamp gli attacchi di replay?

Il timestamp fa parte del contenuto firmato, quindi non può essere alterato senza rompere la firma. Il ricevitore lo confronta con il proprio orologio e rifiuta qualsiasi cosa più vecchia di una tolleranza (comunemente pochi minuti). Questo delimita per quanto tempo una richiesta valida catturata può essere riprodotta; una cache di nonce chiude i replay all'interno di quella finestra.

### Perché è richiesto il confronto a tempo costante?

Un normale controllo di uguaglianza termina in anticipo al primo byte non corrispondente, e un attaccante in grado di temporizzare la risposta può recuperare la firma corretta byte per byte. Un confronto a tempo costante esamina sempre l'intera lunghezza prima di restituire, eliminando il canale laterale di timing. Usa la funzione di confronto timing-safe della piattaforma.

### Come ruoto un segreto di firma dei webhook in sicurezza?

Usa chiavi sovrapposte. Fornisci il nuovo segreto mentre quello vecchio resta valido; durante la sovrapposizione il mittente firma con entrambi o il ricevitore accetta l'uno o l'altro, così nessun webhook legittimo viene scartato. Una volta confermati tutti i producer sul nuovo segreto, ritira quello vecchio. Un prefisso di schema sulla firma rende osservabile la transizione.

### TLS non è sufficiente per fidarsi di un webhook?

No. TLS dimostra che il transport era cifrato e che il client ha raggiunto il tuo server, ma non che la richiesta provenga dalla piattaforma di messaggistica — chiunque può eseguire un POST su un URL pubblico. Solo una firma HMAC a segreto condiviso autentica il mittente e dimostra che il payload non è stato modificato. L'allowlisting di IP è un sostituto debole perché gli IP di egress cambiano.

## Fonti

- [IETF RFC 2104 — HMAC: Keyed-Hashing for Message Authentication](https://datatracker.ietf.org/doc/html/rfc2104)
- [IETF RFC 6234 — US Secure Hash Algorithms (SHA and HMAC-SHA)](https://datatracker.ietf.org/doc/html/rfc6234)

## Correlati

- [Consegna e affidabilità dei webhook](https://www.broadnet.me/it/reference/webhook-delivery-and-reliability)
- [Webhook di stato e inbound dei messaggi](https://www.broadnet.me/it/reference/message-status-and-inbound-webhooks)
- [Autenticazione e firma delle API](https://www.broadnet.me/it/reference/api-authentication-and-signing)
- [Validatore di firma dei webhook](https://www.broadnet.me/it/tools/webhook-signature-validator)
- [Firma DLR con HMAC-SHA256](https://www.broadnet.me/it/answers/hmac-sha256-dlr-signing)
