Un sito web riceve continuamente dati: nomi utente, commenti, descrizioni di prodotti, parametri negli URL e informazioni inviate attraverso le API. Questi dati possono essere innocui, ma diventano un problema quando vengono inseriti nelle pagine senza essere gestiti correttamente.
Una vulnerabilità Cross-Site Scripting (XSS) permette a un attaccante di far eseguire codice JavaScript non previsto nel browser di un altro utente, all'interno del contesto di un sito vulnerabile.
A seconda del tipo di vulnerabilità e dei privilegi dell'utente coinvolto, questo può consentire di manipolare la pagina, leggere informazioni accessibili al codice, effettuare operazioni per conto della vittima o compromettere la sicurezza di una sessione.
Vediamo come funziona l'XSS, quali forme può assumere e quali contromisure sono utili per sviluppatori e amministratori di siti web.
Che cos'è una vulnerabilità XSS?
Il Cross-Site Scripting si verifica quando un'applicazione permette a contenuti non attendibili di essere interpretati dal browser come codice attivo.
Un esempio comune è una pagina che mostra un commento inviato da un utente senza codificare correttamente il contenuto. Se il browser interpreta una parte del commento come HTML o JavaScript invece che come semplice testo, può verificarsi un'esecuzione non prevista.
Il punto fondamentale è che il codice viene eseguito nel contesto del sito vulnerabile. Il browser può quindi attribuirgli le autorizzazioni normalmente associate a quella pagina, nei limiti imposti dalla stessa origine e dagli altri controlli di sicurezza.
Non è necessario che il server venga compromesso direttamente: in molti casi, il bersaglio immediato è il browser della persona che visita la pagina.
Le tre principali tipologie di XSS
1. XSS riflesso (Reflected XSS)
L'XSS riflesso si verifica quando un dato ricevuto da una richiesta viene reinserito nella risposta HTML senza una gestione sicura.
Per esempio, una pagina di ricerca potrebbe mostrare il termine cercato dall'utente. Se il valore viene inserito direttamente nel markup, un input appositamente costruito potrebbe essere interpretato come contenuto attivo.
L'attacco può richiedere che la vittima apra un link preparato ad hoc o effettui una richiesta specifica.
La presenza di un parametro nell'URL non rappresenta di per sé una vulnerabilità: il rischio dipende da come quel parametro viene elaborato e restituito.
2. XSS memorizzato (Stored XSS)
L'XSS memorizzato si verifica quando un contenuto malevolo viene salvato dall'applicazione e successivamente mostrato ad altri utenti.
I punti da controllare possono includere:
- commenti e recensioni;
- profili utente;
- descrizioni e annunci;
- ticket di assistenza;
- messaggi e contenuti condivisi;
- campi gestiti dai pannelli amministrativi.
Questa forma può essere particolarmente pericolosa perché il contenuto può raggiungere più persone senza che ciascuna debba visitare un link specifico predisposto dall'attaccante.
Il problema può coinvolgere anche gli amministratori, se visitano una pagina contenente il contenuto vulnerabile.
3. XSS basato sul DOM (DOM-based XSS)
Nel DOM-based XSS, il problema nasce nel codice JavaScript eseguito dal browser, che utilizza dati non attendibili per modificare la pagina in modo pericoloso.
Un esempio è uno script che legge un valore dall'URL e lo inserisce nel DOM attraverso un'operazione che interpreta HTML, anziché trattarlo come testo.
Questa vulnerabilità può essere presente anche quando il server non restituisce direttamente il dato pericoloso nella risposta HTML: è il codice lato client a creare la condizione vulnerabile.
Per questo motivo, una revisione della sicurezza può richiedere l'analisi sia del codice server sia del JavaScript.
Che cosa può fare un attacco XSS?
Le conseguenze variano in base al contesto, ai dati accessibili e alle protezioni presenti.
Un attacco può essere utilizzato per:
- modificare il contenuto visualizzato nella pagina;
- mostrare form ingannevoli;
- leggere informazioni accessibili al codice JavaScript;
- effettuare richieste utilizzando il contesto autenticato della vittima;
- interferire con operazioni e flussi applicativi;
- tentare di sottrarre credenziali o dati accessibili nel browser.
È importante non confondere XSS con il furto automatico dei cookie. I cookie di autenticazione configurati con HttpOnly non sono leggibili direttamente attraverso le normali API JavaScript del browser.
Tuttavia, HttpOnly non impedisce necessariamente a uno script malevolo di eseguire operazioni per conto dell'utente all'interno della pagina. Per questo la protezione dei cookie deve essere accompagnata dalla prevenzione dell'XSS stesso.
La difesa più importante: codificare l'output nel contesto corretto
Una delle regole fondamentali per prevenire XSS è evitare di inserire dati non attendibili direttamente nel codice HTML o JavaScript.
Quando un'applicazione deve mostrare un testo, dovrebbe trattarlo come testo, non come markup da interpretare.
In PHP, per esempio, un valore destinato a essere visualizzato come testo HTML può essere codificato così:
<?php
$nome = $_GET['nome'] ?? '';
echo htmlspecialchars(
$nome,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
Questa tecnica converte caratteri significativi per HTML in rappresentazioni che il browser visualizza come testo, riducendo il rischio che vengano interpretati come markup.
Il contesto è fondamentale: ciò che è appropriato per un nodo di testo HTML potrebbe non essere sufficiente per un attributo, una stringa JavaScript, un URL o un blocco CSS.
Non esiste una funzione universale da applicare indiscriminatamente a ogni tipo di output. È necessario utilizzare una codifica adatta al contesto specifico.
Perché la validazione dell'input non basta
Validare gli input è utile per verificare che i dati rispettino il formato previsto. Per esempio, un campo che dovrebbe contenere una data può essere controllato per escludere valori non validi.
Tuttavia, non è sempre possibile stabilire in anticipo quali caratteri siano sicuri in ogni contesto.
Un commento, una descrizione di prodotto o un nome potrebbero legittimamente contenere apostrofi, simboli e caratteri Unicode.
La strategia corretta consiste nel combinare:
- validazione dei dati in base alla funzione del campo;
- codifica dell'output nel contesto appropriato;
- uso di API sicure per manipolare il DOM;
- sanitizzazione specifica quando è necessario consentire HTML;
- revisione dei componenti che elaborano contenuti forniti dagli utenti.
La validazione e la codifica dell'output risolvono problemi differenti: entrambe possono essere necessarie.
Attenzione a innerHTML e alle API JavaScript
Nel codice frontend, alcune API interpretano una stringa come HTML e possono creare elementi attivi.
Per esempio, assegnare dati non attendibili a innerHTML può introdurre XSS se il contenuto viene interpretato dal browser.
Quando l'obiettivo è mostrare testo semplice, è preferibile utilizzare API come textContent, che non interpretano il valore come markup HTML.
Se l'applicazione deve consentire HTML formattato, è necessario utilizzare un processo di sanitizzazione progettato per quel contesto e mantenere aggiornate le librerie utilizzate.
La semplice rimozione di alcune stringhe considerate pericolose non è una difesa affidabile: esistono numerosi modi per rappresentare contenuti e markup, e i filtri artigianali possono lasciare passare casi non previsti.
Content Security Policy: una difesa aggiuntiva
La Content Security Policy (CSP) è una politica di sicurezza che permette di stabilire quali risorse una pagina può caricare o eseguire.
Una CSP ben progettata può ridurre l'impatto di alcune vulnerabilità XSS, limitando l'esecuzione di script non autorizzati e l'accesso a risorse esterne non previste.
Tuttavia, non dovrebbe essere utilizzata come sostituto della corretta gestione degli input e degli output.
Una politica permissiva, per esempio una che consente indiscriminatamente script inline, può offrire una protezione molto limitata.
Per applicare CSP in modo efficace, è necessario analizzare le risorse effettivamente utilizzate dal sito e costruire una politica coerente con la sua architettura. La modalità di sola segnalazione può essere utile per osservare le violazioni prima di applicare una politica restrittiva in produzione.
XSS e siti WordPress
WordPress consente a utenti e amministratori di pubblicare contenuti, mentre temi e plugin possono aggiungere form, widget e componenti dinamici.
Questa flessibilità rende importante verificare come i dati vengono elaborati e visualizzati.
Gli sviluppatori WordPress dovrebbero utilizzare le funzioni di escaping appropriate al contesto, come esc_html(), esc_attr() e esc_url(), e le funzioni di sanitizzazione previste per i dati in ingresso.
Quando è necessario consentire HTML limitato, wp_kses() può aiutare a filtrare il contenuto in base a tag e attributi ammessi.
Queste funzioni non sono intercambiabili: la scelta dipende da dove il dato viene utilizzato e da quale formato è legittimo consentire.
Anche i plugin di terze parti devono essere mantenuti aggiornati, perché una vulnerabilità può risiedere in una funzionalità aggiuntiva e non nel core di WordPress.
Come verificare se un sito è esposto a XSS
Una verifica dovrebbe includere i punti in cui dati controllati dall'utente vengono mostrati nel browser.
Tra le aree da esaminare:
- [ ] Form di ricerca e parametri negli URL.
- [ ] Commenti, recensioni e contenuti pubblicati dagli utenti.
- [ ] Profili e campi personalizzati.
- [ ] Pannelli amministrativi e dashboard.
- [ ] Componenti JavaScript che manipolano il DOM.
- [ ] Widget e plugin che visualizzano contenuti esterni.
- [ ] Funzioni che consentono di inserire HTML.
- [ ] Politiche CSP e relativi report.
I test dovrebbero essere effettuati in un ambiente autorizzato, utilizzando payload innocui e controllati. È importante verificare non soltanto se un dato viene visualizzato, ma anche se viene interpretato come codice attivo.
Le scansioni automatiche possono individuare alcuni casi, ma non sempre riescono a rilevare vulnerabilità che dipendono da percorsi applicativi specifici o da interazioni complesse nel frontend.
Conclusione
Il Cross-Site Scripting può compromettere la sicurezza degli utenti anche quando il server non presenta un'esecuzione di codice direttamente sfruttabile. La prevenzione richiede una gestione corretta dei dati, codifica dell'output nel contesto appropriato e attenzione al codice JavaScript.
Con MiHakero puoi esaminare la superficie esposta di domini e applicazioni web alla ricerca di problemi rilevabili dall'esterno. Per valutare in modo completo le vulnerabilità XSS è utile integrare queste verifiche con test applicativi autorizzati, soprattutto nei punti in cui vengono elaborati e mostrati contenuti controllati dagli utenti.