DIEGO CECATO / NOTE DAL CAMPO

HTTP/3 e QPACK: il limite sbagliato si paga in memoria

diego cecato//
HTTP/3 e QPACK: il limite sbagliato si paga in memoria

Un server riceve una richiesta apparentemente piccola. La legge, la decodifica e scopre che per gestirla deve tenere in memoria una quantità di dati enormemente superiore a quella arrivata dalla rete. Il firewall vede poco traffico, il processo vede crescere l’heap. Se l’unico limite che hai progettato è quello sui byte in ingresso, hai appena messo il contatore dalla parte sbagliata del sistema.

È il problema portato alla luce il 20 ottobre 2026 da Sébastien Féry di FoxIO con la ricerca Header Accordions. Il bersaglio non è una misteriosa proprietà insicura di HTTP/3: sono i decoder di QPACK, il meccanismo che comprime gli header, quando non applicano correttamente i limiti alle informazioni dopo la decodifica. Nelle prove del ricercatore sette implementazioni su dieci hanno mostrato problemi; quel rapporto descrive il campione esaminato, non la percentuale dei server vulnerabili su Internet.

La compressione non cancella il costo. Lo sposta.

QPACK, definito nella RFC 9204, evita di trasmettere ripetutamente nomi e valori degli header HTTP. Può sostituirli con riferimenti a una tabella statica o a una tabella dinamica. È una scelta sensata: meno byte da trasferire, meno lavoro sulla rete, comunicazioni più efficienti. Ma il destinatario deve ricostruire quei campi, organizzarli in strutture dati e consegnarli all’applicazione.

La RFC 9114, sezione 4.2.2, specifica che la dimensione di una sezione di header si calcola sommando la lunghezza non compressa del nome e del valore di ciascun campo, più 32 byte di overhead per campo. Non è un dettaglio contabile: significa che cento campi brevi non costano soltanto la somma delle loro lettere. Costano anche le strutture necessarie a rappresentarli. Una implementazione può imporre un limite, ma deve applicarlo alla dimensione corretta e nel momento corretto.

Nel caso esaminato da FoxIO, un blocco HEADERS di circa un megabyte poteva produrre oltre 160 mila campi e circa 15 MiB di memoria trattenuta in quic-go prima della correzione. In altre implementazioni il rapporto fra traffico ricevuto e risorse consumate risultava molto più alto: fino a circa 4.000 volte in uno scenario sperimentale su Bun. Sono misure di laboratorio riferite a stack e configurazioni specifici, non un moltiplicatore universale applicabile a qualsiasi richiesta HTTP/3.

Cinque errori diversi, la stessa idea sbagliata

La parte interessante della ricerca non è la classifica dei prodotti coinvolti. È la ricorrenza di un errore architetturale: una soglia esiste, magari viene persino annunciata al peer, ma non protegge la risorsa che dovrebbe proteggere. Il problema può presentarsi in modi differenti.

  • Si controlla la dimensione del frame compresso e si ignora quanto crescerà dopo la decodifica.
  • Si decodifica tutto, si alloca memoria e solo alla fine si verifica se il limite è stato superato.
  • Si contano i caratteri dei campi, dimenticando l’overhead previsto dallo standard.
  • Una configurazione assente viene interpretata come assenza totale di limiti.
  • Un limite viene pubblicizzato nelle impostazioni del protocollo, ma il percorso effettivo del decoder non lo applica.

È la differenza fra avere una regola e averla collegata al punto in cui serve. Vale per gli header HTTP, ma anche per archivi compressi, parser, code di messaggi, payload JSON e qualunque formato capace di espandersi durante l’elaborazione. La dimensione del dato che entra non coincide necessariamente con la quantità di lavoro che quel dato può imporre.

Un limite che interviene dopo aver consumato la risorsa non è un limite di sicurezza. È un resoconto del danno.

HTTP/3 non è un’unica implementazione

Dire che HTTP/3 è vulnerabile sarebbe comodo, ma tecnicamente impreciso. FoxIO ha esaminato dieci implementazioni, con risultati diversi. Nel suo resoconto del 20 ottobre risultavano corrette quic-go 0.57.0, Cloudflare quiche 0.29.3 e lsquic 4.9.0; per Wireshark, che qui è un analizzatore e non un server HTTP/3 esposto, sono indicate le versioni 4.6.2 e 4.4.12. Nella stessa fotografia temporale il ricercatore segnalava ancora problemi non risolti in Bun 1.4.3, hyperium/h3 e aioquic/mitmproxy.

Le versioni vanno lette come stato della disclosure, non come inventario aggiornato automaticamente al giorno in cui leggerai questo articolo. La segnalazione su oss-security riprende la matrice di correzioni e mitigazioni. Anche l’impatto varia: si va dall’esaurimento della memoria a condizioni che bloccano la CPU, mentre il caso Wireshark richiede un percorso locale di analisi del traffico e non va confuso con una vulnerabilità remota del server.

Questo dettaglio conta quando qualcuno propone una soluzione sbrigativa: disattivare HTTP/3 dappertutto. Può essere una mitigazione temporanea sensata per uno stack esposto e non corretto, ma non è una diagnosi. Alcune implementazioni esaminate, come IIS, Envoy e ngtcp2/nghttp3, non risultavano vulnerabili nei test descritti. Il primo passo è capire quale componente termina davvero HTTP/3 nella tua architettura, non confondere il protocollo con il prodotto che lo implementa.

Il budget deve vivere dove nasce il costo

Una protezione efficace non si limita a impostare una dimensione massima per i dati ricevuti. Deve governare il numero e la dimensione dei campi già ricostruiti, l’overhead delle strutture allocate, il lavoro di parsing e il ciclo di vita dei buffer. E deve farlo progressivamente, prima che la materializzazione diventi irreversibilmente costosa. La differenza fra rifiutare una richiesta troppo grande e costruirla interamente per poi rifiutarla è precisamente la differenza fra prevenzione e spreco.

Non basta esporre una configurazione

Un’opzione chiamata max_field_section_size non è una garanzia se il decoder la ignora, la interpreta diversamente o la applica troppo tardi. La ricerca descrive proprio percorsi nei quali la configurazione esiste ma non si traduce in un budget cumulativo effettivo. Per chi sviluppa librerie e servizi la domanda utile non è soltanto “quanto vale il limite?”, ma “in quale punto del flusso viene verificato e quali allocazioni avvengono prima?”.

Una richiesta attraversa più confini

Proxy, terminatori TLS/QUIC, librerie HTTP e framework applicativi possono vedere rappresentazioni diverse della stessa richiesta. Un limite sul traffico di rete, uno sul frame compresso e uno sull’oggetto finale non sono intercambiabili. Se una soglia esiste soltanto a valle, il componente a monte può già aver sostenuto il costo. Se una soglia esiste soltanto a monte, la decompressione può trasformare un input piccolo in un carico grande. Il controllo va pensato per l’intera catena.

Cosa verificare, senza trasformare la produzione in un laboratorio

Il lavoro operativo parte dall’inventario: quale servizio espone HTTP/3, quale libreria QPACK utilizza, quale versione è effettivamente in esecuzione e dove termina la connessione QUIC. Poi si confrontano quelle versioni con gli advisory dei maintainer e con le correzioni disponibili. Le mitigazioni temporanee indicate da FoxIO per stack ancora non corretti devono essere valutate sul proprio deployment, perché la stessa etichetta “HTTP/3 abilitato” può nascondere componenti molto diversi.

Il secondo controllo riguarda le risorse, non il marketing del protocollo: limiti cumulativi sugli header decodificati, numero di campi, memoria per richiesta e per connessione, tempo di CPU e comportamento al rifiuto. I test di robustezza vanno condotti in ambienti isolati e autorizzati, osservando se una richiesta fuori budget viene fermata prima delle allocazioni pesanti. Non serve riprodurre un attacco su un server pubblico per capire se la protezione è stata progettata bene.

Infine, una patch presente nel repository non significa automaticamente che sia attiva nel processo in produzione. È un tema che ritorna anche quando si parla di runtime PHP e patch realmente caricate. E la memoria può essere consumata da codice che non sembra nemmeno lavorare: ne avevo già discusso a proposito di Cloudflare Workers e risorse trattenute. I casi sono diversi, il metodo di verifica è lo stesso: misurare il sistema che gira, non quello che immagini di aver configurato.

La lezione non riguarda soltanto QPACK

Header Accordions non dimostra che ogni server HTTP/3 sia fragile e non autorizza a dedurre attacchi già avvenuti in produzione. Mostra qualcosa di più utile: quando un formato compatto viene trasformato in oggetti costosi, il punto in cui imponi i limiti determina se l’architettura regge o si limita a contare le macerie. La ricerca di FoxIO offre una dimostrazione concreta, non una nuova legge universale della sicurezza.

La prossima volta che un sistema promette un limite di un megabyte, chiediti un’altra cosa: un megabyte di cosa? Di traffico, di contenuto decodificato, di memoria trattenuta o di lavoro computazionale? Finché non sai rispondere, quel numero è soltanto rassicurante. E i numeri rassicuranti sono ottimi per le slide, molto meno per i sistemi che devono restare in piedi.

Domande frequenti

Che cos'è QPACK in HTTP/3?

QPACK è il formato di compressione degli header usato da HTTP/3. Riduce i byte trasmessi tramite tabelle di riferimenti, ma il destinatario deve ricostruire nomi e valori e sostenere il costo delle strutture dati decodificate.

Header Accordions rende vulnerabili tutti i server HTTP/3?

No. FoxIO ha riscontrato problemi in sette delle dieci implementazioni testate; il risultato non equivale alla percentuale dei server presenti su Internet. Altri stack del campione non risultavano affetti dalle condizioni esaminate.

Quali versioni risultavano corrette nella disclosure di FoxIO?

Al 20 ottobre 2026 FoxIO indicava quic-go 0.57.0, Cloudflare quiche 0.29.3, lsquic 4.9.0 e Wireshark 4.6.2/4.4.12. Le versioni e le mitigazioni vanno ricontrollate sugli advisory dei rispettivi maintainer.

Perché limitare il frame compresso non basta?

Perché un input compresso piccolo può generare molti campi e allocazioni dopo la decodifica. Il budget di sicurezza deve considerare dimensione non compressa, overhead per campo e consumo cumulativo, intervenendo durante il parsing.

Come verificare il rischio nella propria infrastruttura?

Occorre identificare il componente che termina HTTP/3, la libreria QPACK e la versione realmente in esecuzione, confrontare gli advisory e controllare in ambiente autorizzato che i limiti sui dati decodificati siano applicati prima di allocazioni eccessive.