Introduzione alle Cross Site Request Forgery (CSRF/XSRF)

Le vulnerabilità Cross Site Request Forgery, abbreviato CSRF o XSRF, consiste nel far credere alla web application che l'azione richiesta sia stata eseguita da un utente abilitato.

Sommario  
1 Introduzione  
2 Tipi di Attacco  
3 Protezione  
3.1 Referer check  
3.2 Token session  
3.3 Serverside protection  
3.4 CAPTCHA  

4 Links utili    

1. Introduzione  
Le vulnerabilità Cross Site Request Forgery, abbreviato CSRF o XSRF, consiste nel far credere alla web application che l'azione richiesta sia stata eseguita da un utente abilitato.  
Questo papers offre delle ottime basi sulla vulnerabilità offrendo ottimi esempi e listati PHP.    

2. Tipi di Attacco  
Supponiamo che http://test.opencosmo.com abbia uno script che permetta a un utente registrato di invitare un amico via e-mail a riunirsi alla comunità.  

Il form di login HTML si presenterà in questo modo:  

<form method="GET" action="friendmail.php">
    <input type="text"   name="mail" />
    <imput type="text"   name="meso" />
    <input type="submit" value="Invita!" />
</form>

Ed il codice PHP:  
<?php
if(!isset($_GET['mail']) || !isset($_GET['meso'])){
     echo "COmpila tutti i form richiesti!";
} else {
      $mail = htmlentities($_GET['mail']);
      $meso = htmlentities($_GET['meso']);
      mail($mail, "Sei stato invitato!", $meso);
}

?>

In questo caso l'attaccante potrebbe usare il form richiesto per inviare messaggi di spam senza problemi poichè friendmail.php non contiene nessun tipo di controllo sulla richiesta ricevuta.  
È possibile far eseguire alla vittima l'exploit PoC senza che se ne accorga.  Ciò può essere un modo alternativo per bypassare il controllo del login sulla pagina friendmail.php.    

PoC:  
http://test.opencosmo.com/friendmail.php?mail=indirizzo@host.com&meso=Visita Opencosmo.com!    

È possibile eseguire l'exploit PoC in molti e svariati modi, come:  
<img src="http://test.opencosmo.com/friendmail.php?mail=indirizzo@host.com&meso=Visita Opencosmo.com!" />  
<link rel="http://test.opencosmo.com/friendmail.php?mail=indirizzo@host.com&meso=Visita Opencosmo.com!" />  

<script src="http://test.opencosmo.com/friendmail.php?mail=indirizzo@host.com&meso=Visita Opencosmo.com!" />  
<iframe src="http://test.opencosmo.com/friendmail.php?mail=indirizzo@host.com&meso=Visita Opencosmo.com!" />    

In questo caso il codice sarà simile a:  
<?php
$user   = "user";
$pass   = "pass";
$user_p = $_POST['user'];
$pass_p = $_POST['pass'];

if($user == $user_p && $pass == $pass_p){
    if(!isset($_GET['mail']) || !isset($_GET['meso'])){
          echo "COmpila tutti i form richiesti!";
    } else {
        $mail = htmlentities($_GET['mail']);
        $meso = htmlentities($_GET['meso']);
        mail($mail, "Sei stato invitato!", $meso);
    }
} else {
    echo "Dati di login errati";
}
// PS. Il codice non è dei migliori. È solamente una bozza di esempio!
?>

Se in alternativa la richiesta provenisse dall'oggetto $_POST, per condurre un attacco è possibile ricreare il form di login cambiando il parametro 'action' inserendo il sito vittima completo e eseguire il form su un proprio server web.  
<form method="POST" action="http://test.opencosmo.com/friendmail.php">
    <input type="text"   name="mail" />
    <imput type="text"   name="meso" />
    <input type="submit" value="Invita!" />
</form>

Ciò permette di togliere eventuali limiti di lunghezza sull'input ed eventuali validazioni. Per esempio Javascript.    


3. Protezioni  
3.1 Referer check  
Una alternativa, pur essendo insicura se usata come unica soluzione di sicurezza, è il controllo del referer. Il cui codice PHP si rappresenta in questo modo:  
<?php

if(eregi("test.opencosmo.com", $_SERVER['HTTP_REFERER'])) {
    // …
} else {
    echo “CSRF/XSRF!";
    die();
}

?>

Insicura poichè il controllo è facilmente bypassabile, e la tecnica si chiama appunto Referer spoofing!  
Alcuni exploit PoC per bypassare il controllo sono:  
PHP  
header("Referer: test.opencosmo.net");    

PERL  
use LWP;
use HTTP::Request::Common;
$agent = $agent = LWP::UserAgent->new;
$site1 = "http://www.opencosmo.com";
$site2 = "http://test.opencosmo.com";
$res = $agent->request(GET '$site1', 
            Referer => '$site2',);


TELNET  
telnet> o www.opencosmo.com 80
telnet> GET /admin.php HTTP/1.0
telnet> Referer: test.opencosmo.com
telnet> [Enter]


CURL  
$ curl --referer http://wwwk.opencosmo.com/false.php --user-agent "Mozilla/4.0" http://test.opencosmo.com/friendmail.php    

3.2 Tokens  
Un'alternativa più sicura del referer check, sono i token session, cioè delle sessioni che fanno uso dei token.  
Un esempio è la pagina che invia i dati al 'friendmail.php':  
<?php
    session_start();
    $token                  = md5(uniqid(rand(), TRUE));
    $_SESSION['token']      = $token;
    $_SESSION['token_time'] = time();
?>

<form method="POST" action="friendmail.php">
    <input type="hidden" name="token" value="<?php echo $token; ?>" />
    <input type="text"   name="mail" />
    <imput type="text"   name="meso" />
    <input type="submit" value="Invita!">
</form>

Ovviamente l'applicazione PHP dovrà controllare se il token è valido. Questa azione lo farà la pagina 'friendmail.php'  
<?php
if(isset($_SESSION['token'[) && $_POST['token'] == $_SESSION['token']){
    if(!isset($_GET['mail']) || !isset($_GET['meso'])){
        echo "COmpila tutti i form richiesti!";
   } else {
        $mail = htmlentities($_GET['mail']);
        $meso = htmlentities($_GET['meso']);
        mail($mail, "Sei stato invitato!", $meso);
   }
} else {
    echo "CSRF/XSRF!";
}
?>

Se volessimo anche limitare il tempo di validità del token aggiungiamo una nuova stringa in più che imposterà la validità a 5 minuti.  
<?php  
$token_age = time() - $_session['token_time']  
if($token_age <= 300){  
    echo "Token non valido. CSRF/XSRF attack?";  
} else {  
    if(isset($_SESSION['token'[) && $_POST['token'] == $_SESSION['token']){  
        if(!isset($_GET['mail']) || !isset($_GET['meso'])){  
            echo "COmpila tutti i form richiesti!";  
        } else {  
            $mail = htmlentities($_GET['mail']);  
                $meso = htmlentities($_GET['meso']);  
            mail($mail, "Sei stato invitato!", $meso);  
        }  
    } else {  
        echo "CSRF/XSRF!";  
    }  
}  
?>
    

3.3 Serverside protection  
La serverside protection è stata sviluppata e ideata dagli esperti di http://0x000000.com. (Il link all'articolo si trova in fondo alla pagina nella sezione 'links utili')    

Il codice è il seguente ed è ben commentato dall'autore, e consiste in una pagina .htaccess da inserire nella root path del server.  
RewriteEngine On

Options +FollowSymLinks
AddType application/x-httpd-php .php .xkill
# only allow nice people.
RewriteCond %{REQUEST_METHOD} ^(HEAD|TRACE|DELETE|TRACK) [NC]
RewriteRule ^(.*)$ deny.xkill [NC]
# redirect them to a page that reset all request variables:
# where the .xkill is a new mime-type we added that is being treated as PHP to reset/log variables.
# you might want to change the prefix for sub domains.

RewriteCond %{HTTP_REFERER} !^http://(.+\.)?yourwebsite\.com/ [NC]
RewriteRule .*\.(php|asp|jsp)$ deny.xkill [NC]

# additionally we can make sure that no crazy stuff get's requested.
# basically remote file inclusion works the same as CSRF, so we deny filenames.

RewriteCond %{QUERY_STRING} ^(.*)('|<|>|/|\.a|\.c|\.t|\.d|\.p|\.i|\.e|\.j)(.*) [NC]

RewriteRule ^(.*)$ deny.xkill [NC]


3.4 CAPTCHA  
Abbiamo voluto presentare per ultima questa soluzione per vari motivi, tra cui quella che non offre una maggior sicurezza, rispetto ai token per esempio.  
I CAPTCHA, Completely Automated Public Turing test to tell Computers and Human Apart, sono le classiche immagini visuali che si trovano durante le registrazioni di un forum o durante un azione importante coma il cambio di una password.    

Ritornando al discorso della sicurezza; se siamo un attaccante che sta inviando delle richieste via browser, in alcuni casi, ci basterà inserire il codice CAPTCHA per eseguire con successo l'attacco.  
I CAPTCHA servono fondamentalmente per bloccare richieste da parte di bot non autorizati, ciò se noi ci volessimo service di PHP, PERL, C.. o qualsiasi altro linguaggio, per fare un attacco. Dovremmo trovare soluzioni alternative.    

Un semplice esempio di CAPTCHA è reperibile nella sezione 'link utili' del seguente white paper.    


4. Link utili  
http://www.playhack.net/view.php?id=31 - White paper in lingua inglese scritto da Nexus sulle Cross Site Request Forgery.  

http://0x000000.com/index.php?i=484 - Articolo originale sulle Serverside protection.  

http://www.codewalkers.com/c/a/GUI-Code/Simple-PHPCAPTCHA/ Semplice CAPTCHA in PHP  

http://projects.playhack.net/project/3 - PHP Library for Session Riding  protection
http://opencosmo.com/papers-4 - Papers originale 'Modifica Blog: Introduzione alle Cross Site Request Forgery (CSRF/XSRF)'

Privacy Policy