Il Cross-site Scripting (XSS) è un attacco informatico di tipo Injection lato client. L’attacco consiste nell’eseguire script dannosi direttamente tramite il browser Web della vittima “iniettando” codice dannoso in una pagina Web legittima. L’attacco effettivo si verifica quando la vittima visita la pagina Web ed esegue il codice dannoso. La pagina Web diventa quindi un veicolo per fornire lo script al browser dell’utente. Solitamente le pagine più vulnerabili per un attacco CSS sono i forum, le bacheche di messaggi e le pagine Web che consentono un input da parte dell’utente (pagina contatti, commenti, etc…).

Gli attacchi XSS possono anche essere utilizzati per deturpare un sito web. L’autore dell’attacco può utilizzare script malevolo per modificare il contenuto del sito Web o persino reindirizzare il browser a un’altra pagina Web, ad esempio una che contiene codice dannoso, nel tentativo, ad esempio di rubare le credenziali dell’utente (phishing) o installare un malware sul computer del malcapitato oppure un keylogger.

Una pagina Web è vulnerabile a XSS se utilizza l’input utente non opportunamente “disinfettato”.  I linguaggi usati per gli attacchi sono VBScript, ActiveX, Flash e persino CSS. Tuttavia, il più comune è JavaScript, che rappresenta il linguaggio più comune per le UI experience nei siti web.

Cosa può fare l’aggressore con JavaScript?

Le vulnerabilità XSS sono considerate meno pericolose rispetto alle vulnerabilità SQL Injection. Le conseguenze di un attacco XSS su una pagina web potrebbero non sembrare disastrose all’inizio, poichè la maggior parte dei browser Web esegue JavaScript in un ambiente strettamente controllato: JavaScript, infatti, ha un accesso limitato al sistema operativo dell’utente e ai file dell’utente. Tuttavia, ciò non toglie che l’attacco potrebbe rivelarsi estremamente dannoso in casi come i seguenti:

  • JavaScript ha accesso a tutti gli elementi a cui ha accesso il resto della pagina web, compresi i cookies. I cookie vengono spesso utilizzati per memorizzare i token di sessione. Se un utente malintenzionato può ottenere il cookie di sessione di un utente, può impersonare quell’utente, eseguire azioni per conto dell’utente e ottenere l’accesso ai dati sensibili dell’utente.
  • JavaScript può leggere il DOM del browser e apportarvi modifiche arbitrarie. Fortunatamente, questo è possibile solo all’interno della pagina in cui è in esecuzione JavaScript.
  • JavaScript può utilizzare l’oggetto XMLHttpRequest per inviare richieste HTTP AJAX con contenuto arbitrario a destinazioni arbitrarie.
  • JavaScript nei browser moderni può utilizzare API HTML5. Ad esempio, può accedere alla geolocalizzazione dell’utente, alla webcam, al microfono e persino a file specifici dal file system dell’utente. Anche se la maggior parte di queste API richiede l’attivazione da parte dell’utente, un utente sprovveduto può essere facilmente raggirato.

Cross-site Scripting può essere utilizzato anche insieme ad altri tipi di attacchi, ad esempio Cross-Site Request Forgery (CSRF) .

Quali sono i tipi di attacchi XSS?

Esistono tre tipi principali di attacchi XSS:

  • Reflected/non-persistent XSS, in cui lo script dannoso proviene da una richiesta HTTP.
  • Stored/persistent XSS, in cui lo script dannoso proviene dal database del sito Web.
  • DOM-based XSS, in cui la vulnerabilità è presente nel codice lato client anziché nel codice lato server.

Reflected/non-persistent XSS

XSS riflesso è l’attacco più semplice. Si verifica quando un’applicazione riceve i dati in una richiesta HTTP e include tali dati nella risposta immediata in modo non sicuro.

Ecco un semplice esempio di una vulnerabilità:

Supponiamo che una pagina PHP contenga la seguente istruzione:

<?php
echo '<div>' . $_GET["message"] . '</div>'; ?>

 

E supponiamo che venga eseguita una richiesta di tipo HTTP GET di questo tipo

https://gianlucatramontana.it/test.php?message=va+tutto+bene.

L’output, del tutto assolutamente lecito, fornito dal browser sarà il seguente:

<div>va tutto bene</div>

L’applicazione non esegue nessun’altra elaborazione dei dati. Tuttavia, un utente malintenzionato può facilmente costruire un attacco come questo:

https://gianlucatramontana.it/test.php?message=<script>/*+codice+malevolo...+*/</script>

<div><script>/* codice malevolo... */</script></div>

Se l’utente visita l’URL costruito dall’aggressore, lo script dell’aggressore viene eseguito nel browser dell’utente. A quel punto, lo script può eseguire qualsiasi azione e recuperare qualsiasi dato a cui l’utente ha accesso.

Stored/persistent XSS

XSS persistente si verifica quando un’applicazione riceve dati da una fonte non attendibile e include tali dati nelle sue risposte HTTP successive in modo non sicuro.

I dati in questione potrebbero essere inviati all’applicazione tramite richieste HTTP; ad esempio, commenti su un post di blog, nickname utente in una chat room o dettagli di contatto su un ordine cliente. In altri casi, i dati potrebbero arrivare da altre fonti non attendibili; ad esempio, un’applicazione di webmail che visualizza i messaggi ricevuti tramite SMTP, un’applicazione di marketing che mostra i post sui social media o un’applicazione di monitoraggio della rete che mostra i dati dei pacchetti dal traffico di rete.

Di seguito è riportato un semplice esempio.

Supponiamo che un sito web consenta agli utenti di inviare commenti sui post del blog, che vengono visualizzati ad altri utenti. Gli utenti inviano commenti utilizzando una richiesta HTTP come la seguente:

POST /post/comment HTTP/1.1
Host: gianlucatramontana.it
Content-Length: 100

postId=3&comment=post+commento+semplice&name=Gianluca

Dopo che questo commento è stato inviato, qualsiasi utente che visita il post del blog riceverà quanto segue nella risposta dell’applicazione:

<p>post commento semplice - Autore: Gianluca</p>

Supponendo che l’applicazione non esegua nessun’altra pulizia dei dati in input, un utente malintenzionato può inviare un commento dannoso come questo:

<script>/* script malevolo... */</script>

All’interno della richiesta dell’aggressore, questo commento sarebbe codificato in URL come e memorizzato quindi in un campo del database, pronto per essere richiamato ogni qualvolta un utente visiti il post:

comment=%3Cscript%3E%2F*%2Bscript%2Bmalevolo...%2B*%2F%3C%2Fscript%3E

Qualsiasi utente che visita il post del blog riceverà ora quanto segue nella risposta dell’applicazione:

<p><script>/* script malevolo... */</script></p>

Lo script fornito dall’aggressore verrà quindi eseguito nel browser dell’utente vittima.

DOM-based XSS

XSS basato su DOM si verifica quando un’applicazione contiene un codice JavaScript lato client che elabora i dati da una fonte non attendibile in modo non sicuro, di solito riscrivendo i dati nel DOM. Nell’esempio seguente, un’applicazione utilizza JavaScript per leggere il valore da un campo di input e scrivere quel valore in un elemento all’interno dell’HTML:

var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'Hai cercato: ' + search;

Se l’autore dell’attacco può controllare il valore del campo di input, può facilmente costruire un codice dannoso che causa l’esecuzione del proprio script:

Hai cercato: <img src=1 onerror='/* codice malevolo... */'>

In un caso tipico, il campo di input verrebbe utilizzato per inviare lo script attraverso la richiesta HTTP, consentendo all’aggressore di eseguire un attacco utilizzando un URL dannoso, allo stesso modo di XSS riflesso.

Come trovare e testare le vulnerabilità XSS

La stragrande maggioranza delle vulnerabilità XSS può essere rilevata in modo rapido e affidabile utilizzando lo scanner di vulnerabilità web di Burp Suite.

Il test manuale per XSS riflesso e memorizzato normalmente comporta l’invio di un semplice input univoco (come una breve stringa alfanumerica) in ogni pagina che consenta l’input; identificare ogni posizione in cui l’input inviato viene restituito nelle risposte HTTP; e testare ogni posizione individualmente per determinare se l’input opportunamente predisposto può essere utilizzato per eseguire JavaScript arbitrario.

Il test manuale per XSS basato su DOM derivante dai parametri URL comporta un processo simile: inserire un semplice input univoco nel parametro, utilizzare gli strumenti di sviluppo del browser per cercare questo input nel DOM e testare ogni posizione per determinare se esiste un punto debole. Tuttavia, altri tipi di DOM XSS sono più difficili da rilevare. Per trovare, ad esempio, vulnerabilità basate su DOM in input non basati su URL (come document.cookie) o sink non basati su HTML (come setTimeout), può richiedere molto tempo.

Come prevenire gli attacchi XSS

Prevenire lo scripting cross-site in alcuni casi è banale, ma può essere molto più difficile a seconda della complessità dell’applicazione e del modo in cui gestisce i dati controllabili dall’utente. In generale, è probabile che la prevenzione efficace delle vulnerabilità XSS implichi una combinazione delle seguenti misure:

  • Filtro in ingresso all’arrivo. Nel momento in cui viene ricevuto l’input dell’utente, filtrare il più rigorosamente possibile in base a ciò che è previsto.
  • Codifica i dati in uscita. Nel momento in cui i dati di input dall’utente vengono usati nelle risposte HTTP, codificare l’output per evitare che venga interpretato come contenuto attivo. A seconda del contesto di output, potrebbe essere necessario applicare combinazioni di codifica HTML, URL, JavaScript e CSS.
  • Usa intestazioni di risposta appropriate. Per impedire XSS nelle risposte HTTP che non sono destinate a contenere HTML o JavaScript, puoi utilizzare le intestazioni Content-Typee X-Content-Type-Options per assicurarti che i browser interpretino le risposte nel modo desiderato.
  • Politica sulla sicurezza dei contenuti. Come ultima linea di difesa, è possibile utilizzare Content Security Policy (CSP) per ridurre la gravità di eventuali vulnerabilità XSS ancora presenti.