Cómo generar sesiones en PHP de forma segura
Al hacer una auditoría de seguridad web contra un sistema, uno de los frentes de ataque es la gestión de sesiones. La posibilidad de que alguien pueda suplantar la identidad de otro usuario, o interactuar con módulos de la aplicación sin estar autenticado puede provocar dolores de cabeza a los desarrolladores y a la organización.
Existen varios aspectos complejos en la gestión de sesiones, pero todo esto se empieza a simplificar si centralizamos en un único módulo todo el código encargado de esta gestión. Una vez definido el fichero que hará de repositorio central de las funciones de gestión de sesiones, es necesario incluir dicho módulo en todas nuestras páginas web.
Dependiendo de nuestro framework de desarrollo, la inclusión de dichas funciones las realizaremos a través de métodos “autoload”, a través de require() / include(), o de otros mecanismos habilitados para la carga de módulos.
Nuestro código debe garantizar que se inicialice una sesión en cada petición HTTP . Vamos a usar para este ejemplo el fichero security.php que será cargado por todos los módulos de la aplicación web a través de los métodos mencionados en el parrafo anterior.
/* Inicializamos la sesion*/
session_start();
Si queremos asegurarnos de la privacidad de nuestras páginas, y que estas indiquen que no deben ser cacheadas por ningún proxy intermedio, podemos incluir las siguientes directivas.
/* Establecemos que las páginas no pueden ser cacheadas */
header("Expires: Tue, 01 Jul 2001 06:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
En este punto, en el que la sesión del usuario ha sido inicializada en el servidor, y que la información de la sesión ha sido transmitida al usuario a través de una cookie, debemos definir en algún lado de nuestra aplicación una función que se encargue de destruir la sesión del usuario. Esta función deberá ser llamada desde varios sitios, siendo el fundamental la página logout.php de nuestra aplicación ( o el método utilizado para desconectar al usuario), así como la página de login.php, ya que consideraremos que cualquier usuario que acceda a esta página desea iniciar sesión en el software, y debemos invalidar sus credenciales anteriores.
function logOut() {
session_unset();
session_destroy();
session_start();
session_regenerate_id(true);
}
Para destruir una sesión en php debemos borrar todas las variables del array global $_SESSION mediante una llamada a session_unset(). La llamada a session_destroy() eliminará toda la información asociada con la sesión en el servidor.Trás inutilizar la sesion anterior, vamos a crear una nueva sesión con un identificador nuevo con session_regenerate_id(). Estos pasos nos garantizarán que la sesión anterior ha quedado completamente invalidada.
Un ataque de session fixation es aquel en el que un usuario es capaz de robar la sesión de otro usuario y suplantar su identidad. Para protegernos de dichos ataques un primer enfoque que debemos realizar nada mas inicializar la sesión del usuario es guardar ciertas variables de sesión con información del cliente:
$_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
$_SESSION['SKey'] = uniqid(mt_rand(), true);
$_SESSION['IPaddress'] = ExtractUserIpAddress();
$_SESSION['LastActivity'] = $_SERVER['REQUEST_TIME'];
En nuestro caso, los dos campos más importantes son la IP del usuario que realiza la petición HTTP, y la versión de su navegador. Por temas de control de actividad también guardaremos la fecha en la que se realiza la petición al servidor web.
Para protegernos de dichos ataques lo que debemos comprobar en cada petición es que no existan modificaciones en los parámetros del navegador (IP, navegador) , ya que esto significaría que alguien desde una ubicación distinta a la anterior del usuario está intentando hacerse pasar por él. La fecha de la ultima actividad no debe superar un umbral definido por nosotros, como por ejemplo 120 minutos. En caso de que suceda un cambio en alguno de los mencionados parámetros, o que la sesión del usuario haya caducado en el acceso al servidor web, debemos llamar inmediatamente al método logOut() y salir con un exit();
El acceso desde dispositivos móviles puede trastocar los planes de un control exhaustivo de la dirección IP. Un planteamiento alternativo puede ser ignorar el cambio de ip si se ha producido desde un dispositivo móvil, identificable por el User Agent del navegador. En el futuro hablaremos de un control adicional basado en geolocalización y de reglas heurísticas adicionales para determinar si el usuario que se encuentra al otro lado de la conexión es realmente quien se suponía que era al principio.
Ya nos hemos quitado un gran número de problemas para evitar molestos ataques contra la sesión del usuario así que vamos con una última recomendación adicional para proteger una aplicación web frente a ataques de usuarios no autenticados. .
Imaginemos que nuestra página web tiene varias páginas que deben ser accedidas de forma anónima, como por ejemplo login.php , register.php y logout.php, y que trás autenticarse al usuario se establece una variable de sesión $_SESSION[‘registered’] = 1 y empieza a acceder a una serie de recursos protegidos;
Al final de nuestro script security.php podemos comprobar la información de la sesión, de modo que si el usuario no está registrado en la aplicación ( $_SESSION['registered']
) y la página accedida no es ninguna de las mencionadas en el punto anterior, se debe realizar una llamada a exit(). De este modo, un usuario no autenticado no podrá ejecutar ningún script del sistema.
Y os preguntareis, ¿para que vale $_SESSION['SKey']?
Pues para evitar ataques de CSRF.
Descubre nuestro trabajo y nuestros servicios de ciberseguridad en www.tarlogic.com/es/
En TarlogicTeo y en TarlogicMadrid.