DIEGO CECATO / NOTE DAL CAMPO

Il sandbox funzionava. Il problema era chi ascoltava fuori

diego cecato//
Copertina: Il sandbox funzionava. Il problema era chi ascoltava fuori

Puoi isolare un contenuto in un iframe, assegnargli un’origine diversa, impedirgli di leggere il documento principale e avere comunque un problema di sicurezza molto serio. Non perché il browser abbia dimenticato le sue regole, ma perché il codice dell’applicazione ha costruito un ponte sopra quelle regole e ha deciso di fidarsi di chiunque lo attraversasse.

È il punto più interessante della ricerca pubblicata da Parameter il 28 ottobre 2026 su una vulnerabilità nei link di conversazione condivisa di ChatGPT. La dimostrazione, condotta dai ricercatori su account controllati, ha raggiunto dati di una conversazione privata della sessione destinataria. Secondo Parameter, OpenAI ha confermato la correzione il 26 ottobre. Non risulta documentato uno sfruttamento reale contro utenti terzi.

Il titolo facile sarebbe stato che una condivisione ChatGPT poteva compromettere un account. Il titolo utile, per chi progetta software, è un altro: un componente isolato può diventare pericoloso quando un componente privilegiato interpreta i suoi messaggi come istruzioni affidabili. E questa è una questione che riguarda molto più di ChatGPT.

Il sandbox non era rotto

La funzione HTML Preview eseguiva contenuti interattivi in un contesto separato da quello della pagina autenticata. In quella condizione il codice della preview non poteva leggere direttamente il DOM della pagina principale. È esattamente il comportamento che ci aspettiamo dalla separazione tra origini: una pagina non deve poter frugare liberamente nella sessione di un’altra.

La conversazione preparata dai ricercatori conteneva però anche un grafico. Quel grafico caricava MathLive, una libreria matematica con una tastiera virtuale che installava un listener per i messaggi tra finestre. Il canale usato era postMessage, un’API legittima e necessaria per far comunicare documenti che il browser tiene separati.

Il difetto non stava nell’esistenza del canale. Stava nella fiducia accordata dal ricevente: secondo l’analisi tecnica di Parameter, il listener poteva accettare istruzioni di configurazione senza verificare adeguatamente quale finestra le avesse inviate. La preview isolata non aveva accesso diretto alla pagina, ma poteva parlare con un ascoltatore installato dentro la pagina privilegiata.

Il confine di sicurezza non coincide con il bordo dell’iframe. Coincide con ogni punto in cui un dato non fidato può diventare un’azione privilegiata.

Un messaggio non è un permesso

Qui vale una distinzione che in tanti progetti viene trattata come un dettaglio: ricevere un evento non significa autorizzarne il mittente. postMessage permette la comunicazione cross-origin, non certifica che chi invia sia la finestra attesa né che il contenuto del messaggio sia sicuro. La documentazione MDN prescrive di controllare event.origin, quando necessario event.source, e poi validare la struttura dei dati.

Sono tre verifiche differenti. L’origine risponde alla domanda “da quale sito arriva?”. La sorgente risponde alla domanda “è proprio la finestra con cui intendevo comunicare?”. Lo schema del messaggio risponde alla domanda “quale operazione sto autorizzando e con quali valori?”. Se ne manca una, il contratto tra componenti resta ambiguo.

Nella ricostruzione di Parameter, il messaggio consentiva di modificare il layout della tastiera virtuale includendo markup HTML. Un’informazione che avrebbe dovuto restare dato di configurazione finiva così in un punto di rendering del documento principale. Non è un problema astratto di JavaScript: è una transizione da dato ricevuto a HTML interpretato in un contesto con privilegi maggiori.

Il momento in cui il dato diventa documento

L’HTML ricevuto arrivava a un sink di rendering basato su innerHTML. Inserire un tag script con innerHTML non ne provoca automaticamente l’esecuzione; sarebbe scorretto raccontare il caso come se bastasse quella singola istruzione. La catena descritta dai ricercatori utilizzava invece la creazione di un ulteriore documento incorporato, non soggetto alle stesse restrizioni della preview originaria.

È questo il passaggio architetturale che conta: un documento non fidato, inizialmente isolato, riusciva a influenzare la creazione di un secondo documento nel contesto della pagina fidata. Il secondo elemento non ereditava automaticamente le protezioni applicate al primo. A quel punto la separazione iniziale non proteggeva più il percorso completo.

La Content Security Policy aggiungeva un altro ostacolo e impediva gli script inline. Secondo la disclosure, una sorgente esterna già autorizzata dalla policy ha offerto un ulteriore passaggio per eseguire codice. Non serve riprodurre il payload per capire il problema: la CSP ha contenuto una parte del rischio, ma non poteva correggere il fatto che HTML controllato da un mittente non verificato fosse già arrivato nel documento principale.

La gravità si misura con i privilegi, non con l’alert

Un alert che compare nella pagina può dimostrare un’esecuzione di codice, ma non spiega da solo l’impatto. Nella prova controllata, il codice ha operato nel contesto autenticato del sito e ha interrogato API accessibili alla sessione della vittima, recuperando il contenuto di una conversazione privata non condivisa. I ricercatori dichiarano di aver trasferito all’esterno soltanto un piccolo estratto per dimostrare l’esfiltrazione.

È importante non mescolare tre livelli. Dimostrato: accesso a conversazioni private nel test e invio di una porzione dei dati. Potenzialmente possibile: altre operazioni disponibili alle API della stessa sessione, senza che ogni singola azione sia stata dimostrata nel report. Non documentato: abuso della vulnerabilità contro utenti reali al di fuori della ricerca. Trattare queste tre cose come equivalenti è un modo molto efficace per fare rumore e molto poco efficace per fare sicurezza.

La timeline della disclosure indica segnalazione il 18 ottobre, riproduzione da parte di OpenAI il 21, classificazione P1 e conferma della correzione su due versioni frontend il 26 ottobre 2026. Sono informazioni riportate da Parameter; non le presento come esito di una verifica indipendente dei sistemi interni di OpenAI. Simon Willison ha ripreso il caso il 31 ottobre, mettendo in evidenza proprio la concatenazione tra preview, messaggi e HTML.

Cinque controlli che farei su una web app

Il valore di un caso del genere non è la curiosità sul prodotto coinvolto. È trasformare una catena particolare in una verifica ripetibile. Per una piattaforma con editor, widget incorporati, anteprime HTML, dashboard o componenti di terze parti, partirei da questi controlli:

  • Inventario dei listener. Cercare ogni addEventListener('message', ...) e identificare origine consentita, finestra attesa, schema dei dati e azioni che può attivare. Il semplice controllo di un campo type non autentica il mittente.
  • Traccia dei sink. Seguire i dati ricevuti fino a innerHTML, template, srcdoc, URL, navigazione e creazione dinamica di frame. Un wrapper chiamato createHTML non garantisce che il contenuto sia stato sanitizzato.
  • Isolamento per ogni documento. Verificare non solo la preview iniziale, ma anche i documenti e iframe creati successivamente a partire da dati influenzabili dall’utente. L’isolamento non deve sparire al secondo passaggio.
  • Permessi minimi delle API. Chiedersi cosa potrebbe fare uno script se riuscisse a eseguire codice nella pagina autenticata. La difesa del frontend non sostituisce controlli server-side sulle operazioni sensibili.
  • Test tra funzionalità. Provare insieme due componenti che singolarmente sembrano corretti: grafico più preview, allegato più condivisione, widget più editor, plugin più pannello amministrativo. È spesso nell’integrazione che cambia il modello di fiducia.

Non è una checklist magica. Sono domande di progettazione che costringono a esplicitare chi può chiedere cosa a chi. Anche quando le dipendenze sono mantenute da terzi, il confine tra il loro componente e il tuo applicativo resta una responsabilità di integrazione.

Perché il test del singolo componente non basta

Immaginiamo di verificare separatamente i pezzi del sistema. La preview è isolata: test superato. Il grafico funziona: test superato. La tastiera virtuale riceve messaggi: test superato. La CSP blocca script inline: test superato. La condivisione pubblica funziona: test superato. Nessuna di queste osservazioni dimostra che il sistema completo sia sicuro.

Serve una mappa dei passaggi di autorità: quale componente produce un dato, chi lo interpreta, dove viene renderizzato, quale origine assume il documento risultante e quali API diventano raggiungibili. La sicurezza non è la somma di cinque spunte verdi; è una proprietà delle relazioni tra i componenti.

È lo stesso ragionamento che emerge quando una sandbox non coincide con il perimetro reale del sistema o quando un WAF deve essere verificato sul percorso end-to-end. Le tecnologie sono diverse, ma l’errore metodologico è simile: scambiare il buon funzionamento di una difesa locale per una garanzia globale.

La lezione non è smettere di condividere

Secondo i ricercatori, il problema è stato corretto prima della pubblicazione del report. Questo non dimostra che ogni implementazione di postMessage sia insicura, che gli iframe siano inutili o che le CSP non servano. Dimostra una cosa più concreta: una protezione corretta può essere aggirata da un altro pezzo dell’applicazione che concede, senza accorgersene, un privilegio equivalente.

Quando aggiungiamo una preview, un plugin, un grafico o un editor, non stiamo aggiungendo soltanto una funzione. Stiamo aggiungendo nuovi messaggi, nuovi documenti e nuove relazioni di fiducia. Il lavoro serio comincia quando li mettiamo sullo stesso diagramma e chiediamo chi può attraversare ogni confine. Il resto è fiducia concessa per abitudine.

Domande frequenti

Che cosa ha dimostrato la vulnerabilità nei link ChatGPT condivisi?

Secondo la ricerca Parameter del 28 ottobre 2026, un link predisposto poteva far eseguire codice nel contesto autenticato di un altro utente e, in una prova controllata, leggere una conversazione privata non condivisa.

La vulnerabilità era nel sandbox della preview?

Non direttamente: l’isolamento della preview impediva l’accesso al documento principale. Il problema nasceva da un listener privilegiato che accettava messaggi cross-frame senza controllare adeguatamente il mittente.

Quali controlli servono quando si usa postMessage?

Validare event.origin e, quando pertinente, event.source; verificare la struttura e i permessi del messaggio; non interpretare HTML non fidato in contesti privilegiati.

La Content Security Policy da sola avrebbe impedito l'attacco?

No. Nella catena descritta da Parameter la CSP ostacolava gli script inline, ma un’origine esterna autorizzata ha permesso un ulteriore passaggio. La CSP è una difesa aggiuntiva, non un sostituto della validazione dei messaggi.

È stato confermato un attacco contro utenti reali?

Non risultano prove pubbliche di sfruttamento contro utenti terzi. La dimostrazione descritta dai ricercatori è stata condotta con account di test; secondo Parameter, OpenAI ha corretto il problema il 26 ottobre 2026.