Evitar Cross-Site Request Forgeries en PHP
Siempre que hagamos una aplicación web, tenemos que tener muy encuenta las cuestiones de seguridad, sobre todo las formas más conocidas de ataque. Una de estas formas es Cross-Site Request Forgeries, que más o menos viene a decir Falsificación de Petición desde Otro Sitio (quizás la traducción no es exacta, pero creo que lo explica bien).
Este ataque se produce cuando a un usuario se le conceden permisos, confiando en él, pero no teniendo en cuenta que otra gente pueda aprovecharse de ello. Supongamos que tenemos una página de compra de artÃculos, cuya aplicación controla perfectamente los campos de entrada y que tiene una función que realiza la operación de compra de artÃculos.
Tenemos el formulario HTML:
<form action="compra.php" method="POST">
ArtÃculo: <input type="text" name="articulo" />
Cantidad: <input type="text" name="Cantidad" />
<input type="submit" value="Comprar" />
</form>
El primer fallo que solemos cometer es leer las variables de entrada mediante $_REQUEST:
<?php
session_start();
if (isset($_REQUEST['articulo'] &&
isset($_REQUEST['cantidad'])) {
compra($_REQUEST['articulo'],
$_REQUEST['cantidad']);
}
?>
Una forma muy utilizada para realizar un ataque y que al autor del artÃculo le gusta mucho, es mediante el uso de una imagen:
<img src="http://ejemplo.org/compra.php?articulo=CAFETERA&cantidad=1000" alt="ads" />
Con esto conseguimos que el usuario que visita nuestra página tambien haga una petición a la página en cuestión sin que él lo sepa, claro, que esto solo funciona si el usuario a la vez tiene una sesión abierta en la página que se está atacando.
La solución es añadir una marca formada por un número “encriptado” y un tiempo para que tenga que renovarse esta marca. La marca se debe crear y pasar en el formulario por el que se envÃan los datos y a parte se debe controlar su existencia, si coincide y si no ha superado el timeout.
<?php
$marca = md5(uniqid(rand(), TRUE));
$_SESSION['marca'] = $token;
$_SESSION['tiempo_marca'] = time();
?>
<form action="compra.php" method="POST">
<input type="hidden" name="marca" value="<?php echo $marca; ?>" />
ArtÃculo: <input type="text" name="articulo" />
Cantidad: <input type="text" name="Cantidad" />
<input type="submit" value="Comprar" />
</form>
<?php
if ($_POST['marca'] == $_SESSION['marca']) {
$diferencia_tiempo_marca = time() - $_SESSION['tiempo_marca'];
if ($diferencia_tiempo_marca <= 300) {
/* Menos de 5 minutos */
}
}
?>
ArtÃculo original: Security Corner: Cross-Site Request Forgeries
VÃa / backdraft