Cross-site scripting (XSS): cos’è e come funziona
Il cross-site scripting (XSS) è una vulnerabilità di sicurezza informatica che permette di iniettare script non autorizzati, in genere JavaScript, in una pagina web affidabile, facendoli eseguire nel browser di chi la visita. L’attacco sfrutta la fiducia che il browser ripone nel dominio legittimo, aggirando di fatto le protezioni standard del web.
Cross-site scripting in sintesi
- Natura della vulnerabilità: rientra nella categoria delle injection ed è identificata dal codice CWE-79.
- Tipi principali: riflesso, memorizzato (stored) e basato sul DOM, distinti dal punto in cui il payload viene salvato ed eseguito.
- Conseguenze tipiche: furto di sessione, impersonificazione dell’utente, phishing e defacement della pagina.
- Difesa principale: codifica contestuale dell’output, affiancata da una Content Security Policy e da cookie con flag HttpOnly e Secure.
- Attualità: resta tra le vulnerabilità più segnalate anche nelle applicazioni costruite con framework moderni, che riducono ma non eliminano il rischio.
- Rilevamento: si individua con test manuali mirati sui punti di input e con scanner automatizzati dedicati alla sicurezza applicativa.
Che cos’è il cross-site scripting

Il nome della vulnerabilità deriva da una definizione storica legata al furto di dati tra domini diversi, ma oggi il termine copre qualunque iniezione di contenuto eseguibile in una pagina fidata. La causa di fondo non è lo script in sé, bensì la confusione tra dato e codice: un’applicazione web che inserisce input non affidabile in una risposta HTML senza convalida o codifica adeguata permette al browser di eseguire quell’input come se fosse parte del sito originale.
Un campo di ricerca, un commento, un parametro URL o il profilo di un utente possono diventare veicolo dell’attacco se il contenuto viene restituito senza trattamento. Il browser della vittima non distingue lo script legittimo da quello iniettato, perché entrambi arrivano dallo stesso dominio e vengono eseguiti con gli stessi privilegi: cookie, sessione autenticata e accesso al DOM della pagina.
La classificazione tecnica di riferimento è CWE-79, dedicata alla neutralizzazione impropria dell’input durante la generazione di pagine web, e la vulnerabilità rientra stabilmente tra le voci più rilevanti dell’OWASP Top 10, confluita nella categoria più ampia delle injection nelle revisioni più recenti dell’elenco.
L’impatto reale dipende dal contesto applicativo: su un sito informativo con contenuti pubblici le conseguenze restano spesso marginali, mentre su un’applicazione che gestisce dati bancari, sanitari o account con privilegi elevati un singolo script iniettato può portare al controllo completo della sessione dell’utente colpito.
Come funziona un attacco di cross-site scripting
Un attacco XSS segue tre passaggi distinti: l’inserimento del codice malevolo in un punto di input dell’applicazione, l’inclusione di quel codice nella risposta inviata al browser e l’esecuzione dello script da parte del browser della vittima, che lo tratta come contenuto legittimo della pagina.
- L’attaccante individua un campo che accetta testo libero e che l’applicazione restituisce senza trattamento, come una barra di ricerca, un modulo di commento o un parametro nell’indirizzo.
- Inserisce un payload, ad esempio un tag
<script>o un attributo evento comeonerror, costruito per essere interpretato dal browser come markup eseguibile. - L’applicazione include quel valore nella pagina HTML, in una risposta immediata oppure salvandolo per restituirlo in seguito ad altri utenti.
- Il browser della vittima analizza la pagina, riconosce il tag o l’attributo come codice valido e lo esegue con i privilegi del dominio visitato.
Il motivo per cui le liste di blocco sui termini “pericolosi” falliscono quasi sempre è che i vettori possibili sono pressoché infiniti: se si filtra il tag script, un payload può arrivare tramite un tag img con attributo onerror, un elemento svg o un indirizzo che inizia con javascript:. Il problema non è il singolo pattern, ma il fatto che l’output non viene mai codificato per il contesto in cui finisce: testo HTML, attributo, URL, blocco di stile o script inline richiedono regole di codifica diverse.
Nelle vulnerabilità basate sul DOM il meccanismo cambia leggermente: non è il server a restituire il payload, ma uno script lato client che legge una sorgente non affidabile, come il frammento dopo il carattere # nell’indirizzo, e la scrive direttamente nella pagina tramite proprietà come innerHTML. Poiché quel frammento spesso non viene mai inviato al server, i controlli lato server e molti strumenti di filtraggio del traffico non lo intercettano.
I tre tipi principali di cross-site scripting

Le vulnerabilità XSS si dividono in tre categorie in base a dove risiede il payload e a chi colpisce: lo script riflesso arriva da una richiesta immediata, quello memorizzato viene salvato in modo permanente e servito a chiunque visiti la pagina, quello basato sul DOM nasce interamente nel codice JavaScript eseguito dal browser.
XSS riflesso
Nello XSS riflesso, chiamato anche non persistente o di tipo I, il payload proviene dalla richiesta HTTP corrente, tipicamente un parametro nell’indirizzo, e viene restituito nella risposta immediata. Non essendo salvato da nessuna parte, l’attacco richiede un passaggio di consegna: l’attaccante costruisce un link malevolo e lo invia tramite email, messaggio o un sito terzo, confidando che la vittima lo apra.
XSS memorizzato (stored)
Lo XSS memorizzato, o persistente, salva il payload sul server, ad esempio in un commento, in un profilo utente o in un ticket di assistenza, e lo restituisce a ogni visitatore di quella pagina. Non serve alcun link da far cliccare: basta che l’utente apra una pagina normale perché lo script, già presente nel contenuto, venga eseguito. Una variante poco visibile è il cosiddetto blind XSS, in cui il payload viene eseguito solo quando un operatore interno apre un pannello amministrativo o di backend, rendendo l’attacco difficile da individuare senza strumenti di monitoraggio dedicati.
XSS basato sul DOM
Nello XSS basato sul DOM la vulnerabilità non è nel codice del server ma in uno script lato client che legge una sorgente non affidabile, come l’indirizzo della pagina, e la scrive senza trattamento in un punto della pagina che il browser interpreta come markup. Questa categoria è stata descritta per la prima volta nel 2005 ed è diventata una delle più frequenti con la diffusione delle applicazioni a pagina singola, dove gran parte della logica di rendering avviene nel browser.
| Caratteristica | Riflesso | Memorizzato | Basato sul DOM |
|---|---|---|---|
| Dove risiede il payload | Nella richiesta HTTP corrente | Salvato sul server (database, commenti) | Nel codice JavaScript lato client |
| Serve un link da inviare | Sì | No | Spesso sì, ma il payload resta nel frammento URL |
| Visibilità per i controlli server | Visibile nei log del server | Visibile nei dati salvati | Spesso invisibile al server |
| Pericolosità tipica | Media, richiede inganno dell’utente | Alta, colpisce ogni visitatore | Variabile, dipende dal sink usato |
Cosa può fare un attaccante con il cross-site scripting
Un payload che si limita a mostrare un avviso dimostra solo l’esistenza della falla: l’impatto reale emerge quando lo script esegue azioni per conto della vittima all’interno della sua sessione autenticata.
- Furto di sessione: lo script può leggere i cookie di sessione e inviarli altrove, a meno che non siano protetti dal flag
HttpOnly, che impedisce l’accesso tramite JavaScript. - Impersonificazione dell’utente: anche senza accedere al cookie, lo script può compiere richieste autenticate per conto della vittima, come cambiare un’email o aggiungere un amministratore.
- Cattura delle credenziali: il codice può riscrivere la pagina con un modulo di login falso o un listener che registra i tasti digitati, raccogliendo dati inseriti in un form apparentemente autentico.
- Defacement e redirezione: controllando il DOM, lo script può sostituire contenuti, inserire messaggi ingannevoli o reindirizzare verso un sito esterno mantenendo il dominio legittimo nella barra degli indirizzi.
La gravità concreta dipende dalla sensibilità dei dati coinvolti e dal livello di privilegio dell’utente colpito: su un’applicazione che gestisce transazioni bancarie o cartelle cliniche l’impatto è generalmente grave, mentre su un sito con contenuti pubblici e utenti anonimi resta spesso contenuto. Quando l’utente compromesso ha privilegi elevati, un singolo script può portare al controllo completo dell’applicazione e dei dati di tutti gli altri utenti.
Una variante storica che mostra la scala del rischio è il worm che nel 2005 sfruttò una vulnerabilità memorizzata su una piattaforma social, propagandosi automaticamente e superando il milione di profili coinvolti in meno di ventiquattr’ore, senza alcuna azione ripetuta da parte dell’attaccante originale.
Perché il cross-site scripting è ancora rilevante oggi
I framework moderni riducono il numero di vulnerabilità banali grazie a meccanismi di escaping automatico, ma non eliminano il rischio: continuano a esistere funzioni che bypassano intenzionalmente queste protezioni e che, se usate senza sanitizzazione, riaprono la falla.
Tra queste funzioni rientrano l’attributo dangerouslySetInnerHTML di React, le funzioni bypassSecurityTrustAs* di Angular e strumenti analoghi di altri framework pensati per inserire markup grezzo nella pagina. Ogni volta che uno sviluppatore ricorre a queste scorciatoie per gestire contenuti ricchi o markdown, la responsabilità di sanitizzare l’input torna interamente a suo carico.
I dati raccolti dai programmi di bug bounty confermano che il problema resta attivo su larga scala: tra gennaio 2024 e metà 2025 un singolo programma di sicurezza aziendale ha gestito oltre 970 segnalazioni di XSS, pari al 15% di tutti i casi classificati come importanti o critici, con una singola ricompensa arrivata a 20.000 dollari per un caso ad alto impatto come il furto di token di accesso senza alcuna interazione dell’utente.
La severità di una vulnerabilità XSS si valuta incrociando la sensibilità dei dati esposti con le condizioni necessarie per sfruttarla: un payload che richiede zero clic su una pagina con dati riservati viene trattato come critico, mentre uno che richiede più interazioni dell’utente su contenuti pubblici scende a un livello moderato o basso. Questa logica di valutazione contestuale è oggi lo standard adottato dai principali programmi di gestione delle vulnerabilità.
Se sviluppi con un framework moderno: verifica i punti di bypass
Se un’applicazione è costruita con React, Angular o framework equivalenti, la protezione automatica copre la maggior parte dei casi, ma va verificato puntualmente ogni utilizzo di funzioni che inseriscono HTML non elaborato, di URL non validati reindirizzati dall’utente e di componenti di terze parti aggiornati raramente, perché sono questi i punti in cui la protezione predefinita viene disattivata.
Come prevenire le vulnerabilità XSS

Non esiste una singola tecnica che elimina da sola il cross-site scripting: la protezione efficace combina codifica dell’output, sanitizzazione dell’input, una politica rigorosa sui contenuti eseguibili e impostazioni sicure dei cookie di sessione.
Codifica dell’output contestuale
Ogni variabile mostrata nel browser va codificata in base al contesto in cui compare: un valore inserito in un blocco di testo HTML richiede l’entità corrispondente per caratteri come <, > e &, mentre un valore inserito in un attributo, in un URL, in un blocco di stile o in uno script inline richiede una codifica diversa, perché ciascun contesto interpreta i caratteri speciali in modo differente. I framework moderni applicano questa codifica in automatico sulle variabili gestite dal loro sistema di template, ma non coprono i punti in cui il codice manipola direttamente il DOM.
Validazione e sanitizzazione dell’input
Accettare solo dati conformi al formato atteso riduce la superficie di attacco, ma non sostituisce la codifica dell’output: un input che deve contenere markup, come un editor di testo ricco, va elaborato con una libreria di sanitizzazione dedicata che rimuove tag e attributi pericolosi mantenendo la struttura legittima del contenuto.
Content Security Policy
Una Content Security Policy rigorosa, impostata tramite header HTTP, limita le origini da cui la pagina può caricare o eseguire script, riducendo l’impatto di un payload riuscito anche quando la codifica dell’output presenta una falla isolata.
Cookie sicuri per la sessione
Impostare i flag HttpOnly e Secure sui cookie di sessione impedisce che uno script iniettato possa leggerli tramite JavaScript o che vengano trasmessi su connessioni non cifrate, riducendo in modo diretto l’impatto di un furto di sessione. Il funzionamento generale dei cookie e i criteri per gestirli correttamente sono descritti nell’approfondimento sui cookie e sul loro funzionamento.
Se il filtro sull’input non basta: aggiungi la codifica in output
Se un’applicazione si affida solo a un elenco di termini o tag bloccati in ingresso, la protezione resta incompleta: un filtro di questo tipo va sempre affiancato dalla codifica dell’output nel punto esatto in cui il dato viene mostrato, perché è lì che si decide se il browser lo tratterà come testo o come codice.
Come testare e rilevare una vulnerabilità XSS
Un punto di input è sospetto quando un valore inviato dall’utente ricompare nella risposta della pagina senza alcuna trasformazione: individuare questi punti è il primo passo per verificare se l’applicazione è esposta.
- Elencare ogni punto di input dell’applicazione: campi di ricerca, moduli di commento, parametri URL, intestazioni HTTP personalizzate e upload di file con nome visibile.
- Inserire in ciascun punto una stringa univoca e non pericolosa, ad esempio una breve sequenza alfanumerica, per verificare dove e come viene restituita nella risposta.
- Individuare il contesto esatto in cui il valore compare: testo HTML, attributo, URL, blocco di stile o script inline, perché ogni contesto richiede un payload diverso per essere verificato.
- Costruire un payload mirato per quel contesto, ad esempio un tag con un gestore di evento per i contesti HTML, e osservare se il browser lo esegue invece di mostrarlo come testo.
- Ripetere la verifica sia per le richieste immediate sia per i contenuti che vengono salvati e restituiti in seguito, per distinguere una vulnerabilità riflessa da una memorizzata.
Per confermare l’esecuzione dello script si utilizza tradizionalmente una funzione che genera un avviso visibile nel browser; nei contesti in cui il browser impedisce questa funzione, come negli iframe di origine diversa dalla pagina principale a partire dalle versioni più recenti di Chrome, si ricorre a una funzione alternativa che produce comunque un segnale osservabile, come l’apertura della finestra di stampa.
Oltre al test manuale, gli scanner automatizzati di sicurezza applicativa individuano rapidamente la maggior parte delle vulnerabilità XSS riflesse e memorizzate, ma restano meno efficaci sulle vulnerabilità basate sul DOM, perché il payload spesso non raggiunge mai il server e quindi non compare nei log analizzati dallo strumento. In questi casi è necessaria un’ispezione del codice JavaScript lato client, alla ricerca di punti in cui dati provenienti da sorgenti non affidabili vengono scritti in proprietà che il browser interpreta come markup.
XSS, CSRF e SQL injection: le differenze
Cross-site scripting, CSRF e SQL injection condividono la radice di injection, ma colpiscono punti diversi dell’applicazione e richiedono difese differenti.
| Caratteristica | XSS | CSRF | SQL injection |
|---|---|---|---|
| Dove viene eseguito il codice | Nel browser della vittima | Nel browser, ma senza codice iniettato | Nel database dell’applicazione |
| Cosa sfrutta | Mancata codifica dell’output | Fiducia del server nelle richieste autenticate | Mancata parametrizzazione delle query |
| Difesa principale | Codifica dell’output e CSP | Token anti-CSRF e controllo dell’origine | Query parametrizzate |
Nello XSS il browser esegue uno script che l’attaccante è riuscito a inserire nella pagina; nel CSRF non viene iniettato alcun codice, ma il browser della vittima invia automaticamente una richiesta autenticata verso un’azione che l’attaccante ha predisposto, sfruttando il fatto che il server si fida di ogni richiesta proveniente da una sessione valida. La SQL injection, invece, colpisce il database e non il browser, inserendo comandi non previsti in una query costruita concatenando input non controllato.
Entrambe le categorie di injection, XSS e SQL injection, restano attive anche nelle applicazioni più recenti perché condividono la stessa causa di fondo: input non affidabile trattato come se fosse codice fidato, nel browser in un caso e nel motore del database nell’altro.
Errori frequenti e casi limite nella gestione dell’XSS
Alcune situazioni vengono spesso valutate in modo scorretto durante l’analisi di una vulnerabilità XSS, sia dal lato di chi sviluppa sia dal lato di chi la segnala.
Self-XSS: quando non è una vulnerabilità sfruttabile
Il cosiddetto self-XSS richiede che sia la vittima stessa a incollare manualmente il payload nella console degli strumenti per sviluppatori del proprio browser: trattandosi di un’azione deliberata e non innescabile da un attaccante remoto, questo scenario non viene generalmente considerato una falla sfruttabile nei programmi di sicurezza applicativa.
XSS in contesti ristretti o non standard
Uno script eseguito all’interno di un file PDF o solo su un browser obsoleto e non più supportato ha un impatto diverso rispetto a uno eseguito in una pagina HTML standard, perché l’ambiente di esecuzione è molto più limitato e privo dell’autorità del dominio: per essere considerato rilevante, un caso del genere deve dimostrare una fuga concreta verso un contesto di esecuzione più ampio.
Se l’applicazione accetta contenuti generati dagli utenti: aggiungi sanitizzazione HTML
Se un modulo, un commento o un profilo permette di inserire markup o testo formattato, la sola codifica dell’output non è sufficiente quando serve mostrare parte della formattazione: in questo caso il contenuto va elaborato con una libreria di sanitizzazione HTML prima del salvataggio o prima del rendering, così da rimuovere tag e attributi eseguibili mantenendo la formattazione legittima.
Un ulteriore errore frequente è disattivare il supporto al metodo HTTP TRACE sui server web: anche quando l’accesso ai cookie tramite script è bloccato, una richiesta TRACE non disabilitata può comunque restituire i dati del cookie nella risposta, aggirando parzialmente la protezione prevista per quel tipo di dato.
Domande frequenti sul cross-site scripting
Che cos’è il cross-site scripting (XSS)?
È una vulnerabilità che permette di inserire script non autorizzati in una pagina web fidata, facendoli eseguire nel browser di chi la visita con gli stessi privilegi del sito legittimo, inclusi cookie e sessione autenticata.
Quali sono i tre tipi principali di XSS?
Sono riflesso, quando il payload arriva da una richiesta immediata; memorizzato, quando viene salvato sul server e servito a ogni visitatore; basato sul DOM, quando l’intera vulnerabilità risiede nel codice JavaScript eseguito dal browser.
L’XSS è ancora rilevante oggi?
Sì: nonostante l’escaping automatico dei framework moderni, restano funzioni che bypassano la protezione se usate senza sanitizzazione, e i dati raccolti dai programmi di bug bounty mostrano centinaia di segnalazioni gestite ogni anno anche su applicazioni recenti.
Come si previene una vulnerabilità XSS?
La difesa combina codifica contestuale dell’output, sanitizzazione dell’input quando serve markup, una Content Security Policy rigorosa e cookie di sessione con i flag HttpOnly e Secure attivi.
XSS e CSRF sono la stessa cosa?
No: nello XSS il browser esegue uno script iniettato dall’attaccante, mentre nel CSRF non viene iniettato alcun codice ma il browser invia automaticamente una richiesta autenticata verso un’azione predisposta dall’attaccante.
Come si testa manualmente una vulnerabilità XSS?
Si inserisce una stringa univoca in ogni punto di input, si verifica dove ricompare nella risposta senza trasformazione, si identifica il contesto esatto e si costruisce un payload mirato a quel contesto per confermare l’esecuzione dello script.
Quale linguaggio è usato più spesso negli attacchi XSS?
JavaScript è il linguaggio tipico dei payload XSS, perché è quello che ogni browser interpreta ed esegue automaticamente all’interno di una pagina HTML, senza richiedere plugin o componenti aggiuntivi.
L’XSS è una vulnerabilità lato client o lato server?
Dipende dal tipo: riflesso e memorizzato nascono da una gestione scorretta dei dati lato server, mentre lo XSS basato sul DOM è una vulnerabilità puramente lato client, generata da codice JavaScript eseguito nel browser.