Una sencilla protección antiXSS para PHP

Buscando por la red un esquema simple de protección contra ataques XSS encontré PHP Input Filter que ofrece lo justo para impedir este tipo de ataques: un filtrado de los campos que nos interesen sin más complicaciones. Para los que tengan curiosidad, el ataque Cross Site Scripting (XSS) consiste en tratar de inyectar código ejecutable en una página web, normalmente Javascript, con lo que se puede lograr acceso a una base de datos o redirigir al usuario. Veamos un ejemplo, un sencillo formulario para autentificación:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Formulario de prueba XSS</title>
</head>
<body>
<form id="formID" name="formID" method="post" action="acceso.php">
<label>Usuario:
<input name="usuarioID" type="text" id="usuarioID" size="30" />
</label>
<label>
Clave:
<input type="password" name="passwordID" id="passwordID" />
</label>
<input type="submit" name="ok" id="ok" value="OK" />
</form>
</body>
</html>
Este formulario nos solicita un usuario y una clave y llama a acceso.php para verificar los datos:
$usuario = $_POST["usuarioID"];
$pass = $_POST["passwordID"];

if ((strcasecmp($usuario, "user") != 0) || (strcasecmp($pass, "pass") != 0))
 Header( "Location:sin_acceso.php?user=".$usuario);
else
 Header( "Location:ok.html");
Si el usuario o la clave no coinciden se llama a sin_acceso.php donde simplemente se muestra un aviso o a ok.html para seguir adelante. El problema surge en sin_acceso.php:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Vulnerable</title>
</head>
<body>
<strong>Error
</strong>el nombre de usuario <?php echo $_GET["user"] ?> es incorrecto
</body>
</html>
La función echo se ejecutará en el servidor y posteriormente nos será enviada la página, por lo que si la variable $_GET["user"] contiene código ejecutable en Javascript (o cualquier otro que nuestro navegador pueda entender), ésta nos llegará con código insertado potencialmente peligroso. Para verlo en acción ejecuta estos ficheros en una carpeta de tu servidor y en el campo usuario introduce
<script>alert('Código insertado');</script>
Si tu navegador no dispone de protección antiXSS aparecerá una ventana Javascript con el mensaje. A mi me ha funcionado con la versión 12.02 de Opera. Para evitarlo debemos filtrar el nombre de usuario antes de llamar a no_acceso.php de forma que todos los caracteres que contenga sean secuencias de escape, que son, simplificando, los códigos de cada carácter más un código (de escape) para determinar cómo debe interpretarse por un terminal. Lo que nos interesa es que tales secuencias convierten una cadena como '<script>' en '&lt;script&gt;' lo cual deja de tener sentido para un navegador y por tanto no interpreta que ahi hay un código ejecutable.
Esto es precisamente lo que nos ayuda a hacer la clase PHP Input Filter. La tarea de filtrar una cadena puede ser muy laboriosa y su programador nos la ahorra. Para usarla en nuestro ejemplo modificamos acceso.php:
require_once("class.inputfilter.php");

$filtro = new InputFilter();

$usuario = $filtro->process($_POST["usuarioID"]);
$pass = $filtro->process($_POST["passwordID"]);

echo strcasecmp($usuario, "user")." ".$pass;

if ((strcasecmp($usuario, "user") != 0) || (strcasecmp($pass, "pass") != 0))
 Header( "Location:sin_acceso.php?user=".$usuario);
else
 Header( "Location:ok.html");
Si ejecutamos el ejemplo con este sencillo cambio veremos que la ventanita ya no aparece. Podemos también limpiar todas las variables a la vez:
$_POST = $filter->process($_POST);
O conservar las etiquetas que nos interesen:
$filter = new InputFilter(array('em', 'i', 'br', 'b', 'strong'));
$_POST = $filter->process($_POST);
O incluso permitir ciertos atributos de ciertas etiquetas, por ejemplo permitir los enlaces con  href y con id pero nada más:
$filter = new InputFilter(array('a'), array('href', 'id'));
$enlace = $filter->process($_POST['enlace']);
Una clase PHP muy recomendable, con versión para PHP 5 y anterior.

Enlaces: PHP Input Filter, Evitar ataques XSS con PHP Input Filter

No hay comentarios:

Publicar un comentario