La memoria cache avvelenata
Next.js, uno dei principali framework per lo sviluppo di siti Web ad alto traffico, presenta un difetto di progettazione che compromette la cache del sito. Inoltre, la correzione implementata potrebbe non essere efficace... The post La memoria cache avvelenata first appeared on Hackerjournal.it.

Fin dalla nascita del Web uno degli attacchi più comuni, consiste nel colpire il sito istituzionale. Ma non è che chi costruisce siti non lo sappia: esistono vari meccanismi per proteggere un portale da tentativi di defacciamento e soprattutto di Denial of Service. Alcune soluzioni sono generali, valgono più o meno per tutti i siti “importanti”, altre dipendono dall’infrastruttura e dal framework su cui si basa l’applicazione del sito Web. A questo proposito, i due framework più diffusi per i frontend, cioè proprio per i siti Web istituzionali non dinamici, sono Next.js e Vue.js. E il primo dei due sembra, per ora, avere più successo proprio con i siti più grandi, quelli che si aspettano di dover ricevere un maggiore numero di visitatori e magari avere una struttura più complessa. Naturalmente, anche un sito “statico”, è solitamente realizzato con un qualche CMS, che memorizza i contenuti del sito in un database. Soprattutto perché alcuni componenti possono essere modificati anche in tempo reale, per esempio da qualche automazione. Se, però, ogni volta che un utente visita una pagina del sito viene fatta una sfilza di query sul database per recuperare i contenuti e generare l’HTML da generare, il risultato è un carico notevole sul database stesso. Che può anche essere potente e replicato, ma per siti molto visitati diventa estremamente costoso e inefficiente. Per questo motivo, Next.js ha un meccanismo di caching lato server: i componenti React di ogni pagina vengono renderizzati direttamente dal server, che poi memorizza in locale l’HTML completo e fornisce ai vari client che lo interrogheranno direttamente la pagina già renderizzata. Almeno finché la cache non viene invalidata, in quel caso si costruisce di nuovo l’HTML aggiornato. Il vantaggio è che in questo modo gli unici client che insistono sull’infrastruttura più delicata (il backend e il database) sono le varie istanze del frontend, che al massimo saranno qualche decina o qualche centinaio. Ognuna di queste istanze frontend Next.js, con l’HTML renderizzato, può rispondere alle richieste contemporanee di migliaia di utenti. Quindi, se alla fine il traffico complessivo del sito è di milioni di utenti unici contemporanei, le richieste che arrivano al backend sono al massimo poche centinaia e comunque solo quando c’è bisogno di rifare la cache perché un contenuto è cambiato.
Tutto perfetto, quindi? Come si fa a colpire un sistema che è pensato proprio per reggere a un traffico enorme? In realtà, c’è un piccolo difetto di progettazione che può ritorcere il caching contro se stesso.

Next.js ha introdotto un fix per evitare l’auto-poisoning della cache, ma può essere bypassato da un malintenzionato. FONTE: https://github.com/
Un parametro ignorato
Next.js scambia tra client e server un blob binario, chiamato RSC (React Server Component), che serve al browser per sapere come aggiornare il DOM della pagina. Visto che questo blob proviene sempre dallo stesso URL, l’intestazione delle sue richieste HTTP contengono il valore
Rsc: 1 nel parametro Vary, così da da far sapere al browser che questo contenuto può cambiare e non va tenuto nella cache. Per ulteriore sicurezza, però, all’URL viene aggiunto un parametro GET del tipo:
_rsc=valorecasuale
In questo modo la richiesta HTTP risulta fatta, ogni volta, a un URL diverso, perché basta cambiare il valore casuale alla fine dell’URL per essere certi di bypassare la cache del browser o di una eventuale CDN posta davanti a Next.js (che poi si comporta come un browser). Il parametro _rsc è utilizzato come “cache buster”. La domanda da porsi è: perché serve questo parametro? Non bastava l’header Vary? Evidentemente no: Next.js finisce per “avvelenarsi” da solo, in un esempio di cache-poisoning da manuale. Se, infatti, le CDN non sanno di dover aggiornare la propria cache rischiano di continuare a mantenere dei contenuti non più validi, e se vengono indotte a mettere in cache qualcosa che non esiste da quel momento forniranno per sempre agli utenti un errore 404.
Il problema dell’header Vary è che molte CDN commerciali (Cloudflare, Cloudfront, o Akamai, per esempio) tendono a ignorarlo o comunque a rimuoverlo quando passano i contenuti ai browser. Questo significa che la pagina che arriva all’utente non ha questo header e rischia di non essere aggiornata correttamente. Non solo: per come funziona Next.js, è anche possibile spingere la CDN a memorizzare la cosa sbagliata e “rompere” il sito. Infatti, la logica di Next.js prevede che la prima volta che un client (il browser di un utente) raggiunge una pagina riceva l’HTML iniziale, e le volte successive ottenga il blob RSC per aggiornare solo il necessario, chiamando sempre lo stesso URL. Questo però significa che è possibile eseguire una richiesta al server Next.js togliendo il parametro _rsc e aggiungendo l’intestazione Rsc: il risultato è che la CDN memorizza, per l’URL base non l’HTML iniziale (che avrebbe senso tenere in cache) ma il blob binario. Sostanzialmente, il problema è che sono previste due modalità di accesso alla risorsa HTTP, che forniscono due output distinti, un HTML da tenere in cache e un blob da non conservare nella cache. Le due modalità si distinguono per i vari parametri che vengono passati nell’URL o nell’intestazione. Il problema è che se qualcuno costruisce una richiesta malevola, mescolando questi parametri, può convincere la CDN a tenere in cache il contenuto “sbagliato” (il blob) al posto del contenuto “giusto”. E il risultato è che i browser, alla fine, non sanno come interpretarlo e l’utente non vede il sito.

Con una serie di tentativi, è possibile indurre una CDN a memorizzare il blob RSC invece dell’HTML, rendendo il sito di fatto non accessibile. FONTE: https://zhero-web-sec.github.io/
Entità della vulnerabilità
Questo tipo di vulnerabilità è particolarmente subdola: il pericolo è che alcuni aspetti non dipendano nemmeno da chi sviluppa il sito, ma dal funzionamento della CDN. Se ci si rivolge a una CDN commerciale, non c’è sempre la garanzia che non cambi il modo di funzionare in futuro. Inoltre, se ci si dovesse accorgere che il proprio sito è vulnerabile a questo tipo di attacco non è facile modificarne il funzionamento per correggere il problema.
La soluzione
Il problema è legato a un “rimpallo” di responsabilità tra gli autori di Next.js e i progettisti delle CDN. Per Next.js, la responsabilità di interpretare l’intestazione Vary è dei client, quindi di browser e CDN. Per le CDN, la regola di fondo è che i contenuti diversi dovrebbero avere URL diversi a prescindere. L’unica soluzione reale è assicurarsi che la CDN di propria scelta non stia rimuovendo o rifiutando l’header Vary: alcune tra le CDN più famose rispettano questo header, ma solo nei piani a pagamento. In alternativa, si può porre davanti a Next.js un reverse proxy (es: nginx) configurato per mappare correttamente URL in modo da soddisfare le regole della CDN.
Leggi anche: “Una falla Mastodontica”
*illustrazione articolo progettata da FreepikThe post La memoria cache avvelenata first appeared on Hackerjournal.it.