Articles

CORS, XSS e CSRF con esempi in 10 minuti

Questo articolo dovrebbe essere il vostro punto di ingresso per gli standard di sicurezza web esistenti, gli attacchi web più comuni e i metodi per prevenirli. Alla fine, scoprirete anche come e perché Samy era l’eroe di tutti (tranne che di Rupert Murdoch, credo)

Cross-origin resource sharing, o CORS, è una caratteristica di sicurezza di IE10+, Chrome 4+, Firefox 3.5+ o quasi tutte le versioni di browser rilasciate dopo il 2012 tranne Opera Mini.

Quando CORS è configurato sul server che è disponibile sul dominio website.com allora le risorse da quel dominio che sono richieste tramite AJAX devono essere iniziate dalle risorse che sono servite da quello stesso dominio.
CORS

In altre parole, se abilitiamo CORS su domain-b.com e lo configuriamo per permettere solo GET richieste dal dominio domain-b.com allora se volete usare un’immagine disponibile sotto https://domain-b.com/images/example.png in canvas sul vostro sito web che è ospitato su domain-a.com, quell’immagine non sarà caricata per la maggior parte dei vostri visitatori.
Le tue risorse protette da CORS saranno ancora disponibili quando richieste da qualsiasi strumento o browser che non rispetta CORS policy.

Configurazione CORS

CORS sono disabilitate per default, il che significa che non c’è un adeguato gestore di server che configuri CORS, il che significa che non puoi accedere a risorse di origine diversa nel tuo XHR. Fondamentalmente, se non fai nulla o abiliti specificamente CORS solo per domini specifici, allora qualsiasi richiesta AJAX che tenta di accedere alle tue risorse sarà rifiutata perché i browser web sono rispettosi del CORS policy.
Questa è la ragione per cui si incontra il problema CORS quando si inizia a sviluppare SPA utilizzando VueJS e NodeJS. La tua applicazione VueJS è ospitata su http://localhost:8080 e quando provi ad accedere all’applicazione server NodeJS su http://localhost:8000 ottieni “No Access-Control-Allow-Origin header is present” perché quelli sono due diversi ORIGINS (combinazione di PROTOCOL, HOST e PORT).

La soluzione per il problema CORS nella modalità di sviluppo di VueJS è impostare il proxy devServer nel tuo file vue.config.js come segue:

module.exports = { ... devServer: { proxy: 'http://localhost:8000', }, ...}
Entrare nella modalità a schermo intero Uscire dalla modalità a schermo intero

Per impostare CORS in produzione dovresti aggiungere un listener appropriato per la richiesta OPTIONS. Questo ascoltatore dovrebbe inviare la risposta 200 con no body ma con Headers che definirà la vostra politica CORS desiderata:

Access-Control-Allow-Origin: https://domain-b.comAccess-Control-Allow-Methods: GET
Entrare in modalità schermo intero Uscire in modalità schermo intero

Per maggiori informazioni su come configurare CORS controllare https://enable-cors.org/index.html, e per approfondire CORS policycontrollare https://livebook.manning.com/book/cors-in-action/part-1/

XSS

XSS sta per Cross Site Scripting ed è un tipo di attacco a iniezione. È elencato come 7° delle 10 principali vulnerabilità identificate da OWASP nel 2017. Cross site scripting è il metodo in cui l’attaccante inietta uno script dannoso in un sito web affidabile.(sezione aggiornata, grazie Sandor) Ci sono 3 tipi di questi attacchi.

  1. Stored XSS – Vulnerabilità proveniente da input utente non protetti e non sanitizzati che sono direttamente memorizzati nel database e mostrati ad altri utenti
  2. Reflected XSS – Vulnerabilità proveniente da valori non protetti e non sanitizzati da URL che sono direttamente usati nelle pagine web
  3. DOM based XSS – Simile al reflected XSS, valori non protetti e non sanificati da URL usati direttamente nelle pagine web, con la differenza che DOM based XSS non va nemmeno sul lato server

Attacco

1. Stored XSS

Ecco un esempio di attacco. L’attaccante entra nel tuo sito web e trova un campo di input non protetto come il campo dei commenti o il campo del nome utente e inserisce uno script dannoso al posto del valore previsto. Dopo di che, ogni volta che quel valore dovrebbe essere visualizzato ad altri utenti, eseguirà il codice dannoso. Lo script dannoso può tentare di accedere al vostro account su altri siti web, può essere coinvolto in un attacco DDoS o simili. Rappresentazione visiva (fonte geeksforgeeks.org):

XSS example

2. Reflected XSS

Riflected XSS è un attacco che avviene quando l’attaccante scopre una pagina con tale vulnerabilità, per esempio:

URL atteso: https://mywebpage.com/search?q=javascript
Ultra maligna: https://mywebpage.com/search?q=<script>alert('fortunately, this will not work!')</script>

<body>...<div> showing results for keyword <script> document.write(window.location.href.substr(window.location.href.indexOf('q=') + 2))</script></div>......JavaScript results......</body>
Entrare nella modalità a schermo intero Uscire dalla modalità a schermo intero

Dopo la scoperta, l’attaccante adesca l’utente a cliccare su tale URL dannoso e voilà. I dati sensibili dell’utente vengono sfruttati.

Ciclo di vita dell’attacco illustrato nell’esempio fornito da geekforgeeks.com:

Reflected XSS example

3. DOM based XSS

Questo tipo di attacco è uguale a quello riflesso ma con la differenza che la parte malevola URL non sarà inviata al server. Per l’esempio di cui sopra:

Urlp atteso: https://mywebpage.com/search?q=javascript
URL malevolo (XSS riflesso): https://mywebpage.com/search?q=<script>alert('fortunately, this will not work!')</script>
Url (XSS basato su DOM): https://mywebpage.com/search#q=<script>alert('fortunately, this will not work!')</script>

La differenza è nel carattere # usato al posto di ?. I browser non inviano la parte dell’URL dopo # al server, quindi la passano direttamente al tuo codice client.

Protezione

Ogni valore che può essere inserito dall’utente ed è usato nella tua applicazione (sia sul lato server che sul lato client) dovrebbe essere trattato come dati non attendibili e quindi dovrebbe essere processato prima dell’uso! Dovresti fare un controllo di sicurezza nella tua app server e anche nella tua app client!
Come mostrato nella documentazione VueJS da solo fa l’escape delle stringhe prima di ottenere il valore dalla variabile. Le versioni più recenti di Angular eseguono anche l’escape delle stringhe implicitamente, quindi se stai usando Vanilla JS, JQuery o simili dovresti implementare l’escape delle stringhe manualmente.

Ci sono tre approcci più comuni sull’elaborazione dei dati non attendibili sono elencati di seguito e il metodo ideale dipende dal tipo effettivo del campo che devi elaborare.

1. Convalida delle stringhe

La validazione è il metodo in cui l’utente definisce un insieme di regole e richiede che i dati non attendibili soddisfino tali regole prima di andare avanti. Questo metodo va bene per valori numerici, username, email, password e campi simili con un insieme concreto di regole di sintassi.

Controlla le librerie esistenti per il tuo framework prima di considerare di scrivere validatori per conto tuo.

2. String escape

Il metodo Escape è utile nei casi in cui dovete permettere all’utente di usare i segni di punteggiatura. Questo metodo passa attraverso la stringa e cerca i caratteri speciali, come < > e li sostituisce con il nome appropriato dell’entità di carattere HTML. Ecco una funzione di base che potresti usare:

function escapeText(text) { return text.replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;')}
Entrare nella modalità a schermo intero Uscire dalla modalità a schermo intero

Ancora una volta, controlla le librerie esistenti prima di scrivere le tue.

3. Sanitizzazione delle stringhe

La sanitizzazione delle stringhe è usata quando l’utente può inserire alcuni tag HTML nei suoi commenti, articoli o simili. Il metodo sanitize passa attraverso il testo e cerca i tag HTML specificati dall’utente e li rimuove. Una delle librerie più popolari che utilizza questo approccio è Google Closure.
Questo metodo è costoso in termini di risorse e considerato dannoso, quindi fate più ricerche prima di sceglierlo.

I browser web (nessuna fonte disponibile da quale versione, IE ha risolto questo problema nel 2014.) evadono automaticamente gli URL prima di inviarli al lato server e li rendono disponibili anche nell’oggetto window.location, quindi il 2° e 3° tipo di attacco sono qui solo per essere consapevoli di loro e per rendere chiaro che anche i parametri URL dovrebbero essere trattati come dati non affidabili.

Per informazioni più dettagliate sugli XSS e su come proteggere adeguatamente la vostra applicazione se ruotate molti dati non attendibili, controllate il cheatsheet di OWASP sulla prevenzione degli XSS.

CSRF

Cross site request forgery o CSRF è un tipo di attacco che si verifica quando un sito web, un’email, un blog, un messaggio istantaneo o un programma malevolo induce il browser web di un utente ad eseguire un’azione indesiderata su un altro sito affidabile dove l’utente è autenticato. Questa vulnerabilità è possibile quando il browser invia automaticamente la risorsa di autorizzazione, come il cookie di sessione, l’indirizzo IP o simili con ogni richiesta.

ATTACCO

Supponiamo che l’utente abbia effettuato l’accesso alla vostra applicazione web di borsa non protetta e che stiate usando un cookie di sessione o un cookie JWT per l’autenticazione. L’attaccante usa anche il vostro servizio ed è in grado di controllare come funziona la vostra API. L’attaccante inganna l’utente ad eseguire lo script (cliccando sul link SPAM nell’email o simili) che invierà la richiesta alla vostra API https://www.stockexchange.com/users/withdraw?how_much=all&address=MY_ADDRESS (terribile design dell’API, non chiedetelo). Dal momento che la richiesta viene eseguita dal browser che invia il payload di autenticazione con ogni richiesta, il vostro server web stockexchange autenticherà l’utente con successo ed eseguirà la transazione e l’utente ingannato perderà tutto il suo saldo senza nemmeno esserne consapevole perché tutto è avvenuto in background. Rappresentazione visiva (fonte miro.medium.com)
CSRF attack

PROTEZIONE

Per fortuna ci sono modelli facili da implementare che impediscono questi attacchi web. Uno dei modelli più comuni è l’uso di CSRF token. Fondamentalmente la procedura è la seguente:

  1. Genera un token unico per ogni richiesta dell’utente, chiamato CSRF token.
  2. Memorizzalo in modo sicuro sul server e rimandalo all’utente come payload della risposta.
  3. Memorizza CSRF token sul lato client.
  4. Quando l’utente cerca di eseguire una qualsiasi richiesta di cambiamento di stato*, invia quella CSRF token con la richiesta come payload.
  5. Prima di eseguire quella richiesta sul lato server controlla se CSRF token è presente ed è valida.

Questo è il modo più semplice per prevenire attacchi CSRF per tutti gli utenti.

Se avete a che fare solo con visitatori che usano browser moderni, allora potete fare affidamento sull’attributo SameSite del cookie di sessione (grazie Gergely)

Siccome le risposte del server sono processabili nella risposta XHR, allora non c’è protezione contro gli attacchi CSRF se la vostra applicazione web è XSS vulnerabile!

Per approfondire controlla il cheatsheet di OWASP sul CSRF.

BONUS

Corto documentario su Samy, autore del worm che ha distrutto MySpace nel 2005 abusando della vulnerabilità XSS, superando la difesa CSRF di MySpace.
https://youtu.be/DtnuaHl378M

Più informazioni sul worm di Samy
https://samy.pl/myspace/tech.html