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>
<?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);
}
?>
http://test.opencosmo.com/[email protected]&meso=Visita Opencosmo.com!
<img src="http://test.opencosmo.com/[email protected]&meso=Visita Opencosmo.com!" />
<link rel="http://test.opencosmo.com/[email protected]&meso=Visita Opencosmo.com!" />
<script src="http://test.opencosmo.com/[email protected]&meso=Visita Opencosmo.com!" />
<iframe src="http://test.opencosmo.com/[email protected]&meso=Visita Opencosmo.com!" />
<?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!
?>
<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>
<?php
if(eregi("test.opencosmo.com", $_SERVER['HTTP_REFERER'])) {
// …
} else {
echo “CSRF/XSRF!";
die();
}
?>
header("Referer: test.opencosmo.net");
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> o www.opencosmo.com 80
telnet> GET /admin.php HTTP/1.0
telnet> Referer: test.opencosmo.com
telnet> [Enter]
$ curl --referer http://wwwk.opencosmo.com/false.php --user-agent "Mozilla/4.0" http://test.opencosmo.com/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>
<?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!";
}
?>
<?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!";
}
}
?>
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]