Añadir un token a los formularios en php

Hoy me he llevado un susto importante, pensaba que una de mis webs había sufrido un ataque inyección de código, pero era bastante poco probable, el formulario tiene una función de seguridad bastante potente, pero mi compañero me ha recomendado que a los formularios les ponga un token para evitar los ataques csrf… y bueno pues ya que me he puesto con ello, vamos a ver que es eso de ataques csrf y como introducir un token a los formularios para evitarlos

¿Qué es un ataque csrf?

Según Wikipedia:

El CSRF (del inglés Cross-site request forgery o falsificación de petición en sitios cruzados) es un tipo de exploit malicioso de un sitio web en el que comandos no autorizados son transmitidos por un usuario en el cual el sitio web confía. Esta vulnerabilidad es conocida también por otros nombres como XSRF, enlace hostil, ataque de un click, cabalgamiento de sesión, y ataque automático.

De modo que para evitarlos es importante tener un poco de organización y hacer los por medio de un token…

¿Cómo implementar el token?

El token debe de ser único por sesión de usuario, ya que es la marca que va a hacer que nuestros formularios se envien correctamente.

Para ello a mi me gusta crear una variable de sesión con  la id del usuario y la hora y minuto. de la siguiente forma:

Nota: Para este ejemplo no voy a usar validaciones ni más medios de seguridad que los que proporciona el token.

Comenzamos creando una estructura básicas de archivos en este caso vamos a tener:

  • index.php donde se va a generar el token y se abre la sesión
  • formulario.php donde se encuentra el formulario y el token en un campo hidden
  • envio.php donde envía los datos el formulario

Index.php

<?php
session_start();
$hora = date('H:i');
$session_id = session_id();
$token = hash('sha256', $hora.$session_id);

$_SESSION['token'] = $token;

echo $_SESSION['token'];
?>
<br>
<a href="formulario.php">formulario</a>

Aquí podemos ver lo siguiente.

  • Comenzamos abriendo una sesión, declarando una variable $hora que recoje la hora actual del sistema y una variable $session_id que recoje la id de la sesión.
  • Tras esto creamos el token por medio de la funcion hash() que tiene dos páramateros
    • El tipo de codificación que queremos, ya sea md5, sha256…
    • La cadena que queremos codificar
  • Finalmente guardamos nuestro token dentro de una variable de sesión.

formulario.php

<?php
session_start();

?>
<form method="POST" action="envio.php">
	<label>Nombre</label><input type="text" name="nombre">
	<input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
	<input type="submit" value="enviar">
</form>

Continuamos con nuestra sesión abierta y procedemos a crear nuestro campo oculto que lleva como valor nuestro token.

Este archivo no tiene nada más digno de mención…

envio.php

<?php
session_start();
$token = $_POST['token'];

if($_SESSION['token'] == $token){
	$nombre = $_POST['nombre'];
	echo "Hola " . $nombre;
}else{
	echo "Has intentado acceder sin cumplir con el token";
}

Aquí es donde recogemos nuestro token y lo comparamos con la variable de sesión que hemos creado, si es igual, entonces recogemos el resto de datos y trabajamos con ellos, en caso contrario lo que hacemos es lanzar un mensaje de aviso o lo que vayamos a necesitar, no olvidemos que este sistema de token lo que hace es evitar que se envíen datos desde un sitio externo al controlador para atacarlo.

Hay que considerar que cada vez que el usuario pase por el index va a crear un nuevo token, lo cual también podría controlarse, y que con ese sistema una vez que se destruye la sesión algo que no hago en el código, ya no serviría el token.

Seguramente haya ejemplos mucho mejores en la red, con muchos más datos y pormenores, pero ya me conocéis, me gusta simplificar los problemas al mínimo para ir teniendo una serie de piezas con las que montar nuestros códigos sin necesidad de tener luego que ir destripando los ejemplos.

Espero que os haya servido, y si tienes dudas, sugerencias… déjalas en los comentarios, y comparte este artículo.