Prevención CSRF

CSRF significa Cross-Site Request Forgery, esta es una vulnerabilidad que puede existir en cualquier lenguaje de programación y en cualquier programa en el que no se ha tomado medidas para prevenirlo.

En este tipo de ataques alguien genera un enlace con información que puede hacer que realices una acción sin tu consentimiento en algún sistema en el que ya te hayas autenticado.

CSRF busca modificar información, por ejemplo cambiar algún dato como tu dirección de email, hacer una transacción, borrar información, etc. Para comprender esto vamos a ver un ejemplo.

Voy a mantener el código lo más simple posible, asi que no habrá bases de datos ni nada sobre las mejores practicas, solo el código absolutamente necesario. Usaremos dos archivos de php login.php e index.php, los cuales se lista a continuación

<?php //Archivo login.php
session_start();
  if (isset($_POST['email']) && isset($_POST['clave']))
  {
    //Validar acceso
    if ( $_POST['email'] =='[email protected]' && $_POST['clave'] = '123' )
    {
      //El acceso esta bien
      $_SESSION["autorizado"] = 1;
      header('Location: index.php');
      die('autorizado');
    }
    else {
      echo "email o clave incorrectas";
    }
  }
?>

<form class="" action="login.php" method="post">
  email
  <input type="email" name="email" value=""><br>
  clave
  <input type="password" name="clave" value=""><br>
  <input type="submit" name="" value="Entrar">
</form>
<?php //Archivo index.php
session_start();

  //Si no esta autorizado, se envia a pantalla de login
  if ($_SESSION["autorizado"] != 1)
  {
    header('Location: login.php');
  }

  //Si recibe un email, se actualiza
  if (isset($_GET['email']))
  {
    echo "Tu nuevo email es " . $_GET['email'];
  }

?>

<form action="index.php" method="get">
  Tu email:
  <input type="email" name="email" value="">
  <button type="submit" name="button">Guardar</button>
</form>

Como ves los archivos son muy simples, el primero muestra un formulario y valida que ingreses el email [email protected] y la clave 123, si lo haces, te envía a la pagina index.php

Luego la página index.php primero valida si ya has iniciado sesión, sino te envía a la pantalla de login, luego si detecta que ya has usado el formulario, entonces actualiza tu email según el dato que ha recibido (en este caso solo muestra un mensaje diciendo que lo cambió), finalmente está el código para mostrar el formulario.

Ahora todo se ve bien, si pruebas el código, solo puedes cambiar tu email si entras al sistema con tus datos. Pero ahora si ingresas esto en la barra de direcciones http://localhost/index.php?email=php%40hacked.com, y verás que has sido atacado con éxito.

Ejemplo CSRF

Esto solo funciona si la víctima ha iniciado sesión en el sistema y el atacante prueba con cientos de personas al azar esperando que alguna caiga en la trampa.

Como evitar ataques CSRF en tu aplicación

Algunas personas piensan que con usar el método POST en los formularios, la aplicación ya está protegida contra este tipo de ataques, pero esto no es cierto, usando Javascript puedes hacer que una persona envíe parámetros POST a una pagina web, con solo presionar un botón o hacer clic en una imagen dentro de otro sitio.

La única forma efectiva de hacerlo es mediante tokens (valores aleatorios) generados por la aplicación y anexados al formulario que muestra la aplicación, luego si al procesar los valores del formulario, el token que se recibe corresponde al token de la aplicación, eso significa que la petición es auténtica.

Para implementar esta medida de seguridad, primero vamos a crear una clase, en mi caso tengo un archivo llamado csrf.php con este código:

<?php
class csrf
{
  //Genera una cadena de texto aleatoria
  public static function getTokenCSRF(){
      $token = hash('md5', uniqid());
      $_SESSION['token-csrf'] = $token;
      return $token;
  }

  //Verifica si se ha recibido un token igual que se genero aleatoreamente
  public static function checkTokenCSRF($token)
  {
    if (!empty($_SESSION['token-csrf']) && $token ===  $_SESSION['token-csrf'])
    {
      unset($_SESSION['token-csrf']);
      return true;
    }
    return false;
  }
}

?>

Ahora tengo que modificar todos mis archivos en donde exista un formulario o en donde se procese información enviada por un usuario, en el ejemplo anterior el formulario quedaría de esta forma:

<?php
session_start();
  //Paso 1: Incluir archivo para tokens
  include "csrf.php"; 

  //Si no esta autorizado, se envia a pantalla de login
  if ($_SESSION["autorizado"] != 1)
  {
    header('Location: login.php');
  }

  //Si recibe un email, se actualiza
  // Paso 2: Validar token 
  if (isset($_GET['email'], $_GET['token']) && csrf::checkTokenCSRF($_GET['token']))
  {
    echo "Tu nuevo email es " . $_GET['email'];
  }

?>

<form action="index.php" method="get">
  Tu email:
  <input type="email" name="email" value="">
  <button type="submit" name="button">Guardar</button>
  //Paso 3: Incluir token en el formulario
  <input type="hidden" name="token" value="<?php echo csrf::getTokenCSRF() ?>">
</form>

Como ves, se siguen 3 pasos:

  1. Incluir archivo csrf.php
  2. Validar el token usando la funcion csrf::checkTokenCSRF y pasando como referencia el token que recibimos
  3. Incluir un campo oculto y el token que generemos con la función csrf::getTokenCSRF

Ahora intenta actualizar los datos (email) usando el formulario y verás que todo funciona igual, pero si intentas ingresar por medio de la barra de direcciones ingresando la ruta completa y los parámetros, verás que de esa forma ha dejado de funcionar y ya estas protegido contra ataques de tipo CSRF.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *