Ejemplo y prevención de secuestro o robo de sesión en PHP

Introducción

En la mayoría de nuestras apps escritas en PHP utilizaremos sesiones. Las sesiones sirven para guardar datos que persisten aunque el usuario refresque la página. En este post veremos cómo robar una sesión y cómo prevenirlo.

Las sesiones generan una cookie y con dicha cookie identificamos al usuario. Por ejemplo, si yo inicio sesión se me da la cookie asd123 y si otro usuario inicia, se le da la cookie asd666 (son ejemplos).

Ahora supongamos que el usuario tiene permisos de administrador, y yo no. Pero si le robo su cookie y me la pongo a mí mismo, PHP pensará que soy el usuario administrador.

En otras palabras, inicialmente yo tenía la cookie asd123 y el usuario la asd666. Se la robo y ahora yo tengo la asd666.

Vamos a ver un ejemplo y prevención de secuestro o robo de sesión en PHP

Requisitos

Para que podamos robar la sesión a gusto, necesitamos dos cosas: que se pueda inyectar código HTML y que no se regenere el id de sesión.

Veremos un ejemplo práctico en donde un programador perezoso deja estas dos vulnerabilidades.

Probar y descargar ejemplo

Puedes probar el ejemplo por ti mismo. El zip contiene todo el código fuente de forma segura. Si quieres dejarlo vulnerable simplemente quita el strip_tags al imprimir los mensajes y comenta la línea de session_regenerate_id

Te dejo el código en GitHub: https://github.com/parzibyte/robo_sesion_php

Ejemplo y prevención de secuestro o robo de sesión en PHP

Será un sitio muy sencillo que tendrá 2 roles. Uno del usuario administrador cuyos datos serán:

  • Usuario: admin
  • Contraseña: 0UqiNBK8iQRrtK1yvHosV79h3

Y uno de un usuario normal, cuyos datos serán:

  • Usuario: user
  • Contraseña: 4PPvLoH19Jvbhig3rebUZnD63

No vamos a meternos con bases de datos ni estilos, porque lo que veremos es cómo robar sesiones, no cómo hacer un login y mantener la sesión.

Será una página en donde se escriben simples mensajes y todos los usuarios los pueden ver. Por simplicidad y para no meternos con bases de datos (aunque si quieres ver bases de datos aquí hay un tutorial con MySQL) guardaremos todo en un fichero de texto (aquí el tutorial).

Lo que haremos será que, sin saber la contraseña del administrador, el usuario podrá tener los privilegios del mismo. Esto será posible porque el usuario escribirá un script en una página en donde no debería.

Programar inicio de sesión

Hagamos primero el formulario para iniciar sesión. Ya en PHP comprobaremos si usuario es “admin” o “user” y en caso de que sí o no, pondremos una variable en el arreglo superglobal $_SESSION.

El formulario HTML queda así:

Como vemos, se envía a login.php que se ve así:

Tablero

En caso de login exitoso, se redirige al tablero en donde mostramos los últimos mensajes. Damos la opción de salir, y de agregar un nuevo mensaje.

Para salir es este código:

El formulario para agregar un nuevo mensaje:

Y el archivo que guarda el mensaje:

Con eso ya tenemos una interfaz buena. Me recuerda a las páginas de hace 20 años.

Interfaz del tablero
Interfaz del tablero

Con eso ya tenemos para agregar. Ahora sí viene lo bueno

Ataque XSS

Muy bien, entonces el usuario va a agregar un mensaje malicioso. Pero el admin no lo notará.

Para ello usaremos una etiqueta script que nos enviará a otro archivo PHP (generalmente debería estar en otro servidor, uno que sea del atacante, pero para efectos de simplicidad lo guardaremos en el mismo)

Entonces el mensaje es:

El usuario malicioso se mandará las cookies a guardar_cookies.php. Ese script será ejecutado cuando el administrador abra la página.

Entonces ingresamos el mensaje y no se verá nada sospechoso, pero nuestro script ya está ahí:

Tablero con script invisible
Tablero con script invisible

Ya que si vemos el código fuente:

Código fuente en donde se ve el script para robar sesión
Código fuente en donde se ve el script para robar sesión

Nota: en el código utilizo fetch para enviar las cookies.

Ahora falta programar el archivo guardar_cookies.php y tomar lo que hay en $_GET:

Nota: para escribir el archivo usamos file_put_contents.

¿Qué falta? esperar a que el admin inicie sesión, tomar su cookie y con ello modificar la nuestra.

Nosotros nos logueamos como usuario normal. Luego esperaremos a que el admin inicie sesión y tomaremos su cookie para usarla como nuestra, entonces en el tablero nos dirá “hola admin” en lugar de “hola user”.

Admin es atacado

Entonces el admin entra y ve los mensajes, no sospecha nada malo:

Tablero de administrador
Tablero de administrador

Pero ahora el atacante ve las cookies e inicia sesión aparte, en otro navegador en otra parte del mundo. La cookie guardada:

Cookie recibida por el atacante
Cookie recibida por el atacante

Iniciamos como usuario normal en otra parte, por ejemplo el modo incógnito:

Iniciar sesión en otro navegador
Iniciar sesión en otro navegador

Hora de cambiar nuestra cookie. Para ello sólo tenemos que hacer que document.cookie sea igual a lo que se guardó en el archivo de texto.

Yo lo cambiaré con la consola de depuración:

Cambiar cookie para secuestrar sesión
Cambiar cookie para secuestrar sesión

Ahora refrescaré mi página en donde estaba como usuario:

Sesión robada o secuestrada con éxito
Sesión robada o secuestrada con éxito

Así de fácil fue secuestrar la sesión del usuario administrador. Esto fue un ejemplo muy simple, pero ahora imaginemos una aplicación para transferir dinero en donde el administrador haga las transferencias, podríamos hacer todo lo que él hace.

Prevención de secuestro o robo de sesión

Podemos tomar muchas medidas. Primero, para prevenir el ataque XSS eliminamos las etiquetas HTML con strip_tags:

http://php.net/manual/es/function.strip-tags.php

Entonces en el tablero haríamos esto:

Así cualquier mensaje con HTML quedaría eliminado y este sería el código fuente al verlo:

Como vemos, ya no permite el uso del script y cuando vemos los mensajes notaremos algo sospechoso:

Etiqueta de script eliminada
Etiqueta de script eliminada

Y por si se diera el caso, también podemos regenerar el id de la sesión para que aunque la cookie sea enviada no sea válida porque se regenerará.

Entonces en el tablero llamamos a session_regenerate_id:

Finalmente podemos agregar una capa extra con un token CSRF: Explicación y prevención de CSRF

Recuerda que en el inicio del post dejé el código fuente para que puedas probar todo por ti mismo.

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

1 comentario en “Ejemplo y prevención de secuestro o robo de sesión en PHP”

Dejar un comentario