## Gestión básica de sesiones

### Seguridad de sesiones

El módulo de sesión no puede garantizar que la información almacenada en una sesión sea vista únicamente por el usuario que ha creado la sesión. Medidas adicionales son necesarias para proteger la confidencialidad de la sesión, según el valor que se le asigne.

La importancia de los datos almacenados en una sesión debe ser evaluada y protecciones adicionales pueden ser desplegadas; esto tiene obligatoriamente un costo como ser menos práctico para el usuario. Por ejemplo, para proteger a los usuarios de una táctica simple, la directiva session.use_only_cookies debe ser activada. En este caso, las cookies deben ser activadas obligatoriamente lado-cliente sino las sesiones no funcionarán.

Existen varias formas de divulgar identificadores de sesión a terceros. Por ejemplo, inyecciones Javascript, identificadores de sesión en las URLs, sniffing de paquetes, acceso físico al dispositivo, etc. Un identificador de sesión divulgado permite a un tercero acceder a todos los recursos asociados con dicho identificador. Primero, las URLs que contienen los identificadores de sesión. Si hay enlaces a sitios o recursos externos, la URL que incluye el identificador de sesión debe ser almacenada en los logs referrer del sitio externo. Si estos datos no están cifrados, los identificadores de sesión serán transmitidos en texto plano por la red. La solución aquí es implementar SSL/TLS en el servidor y hacerlo obligatorio para los usuarios. HSTS debe ser utilizado para mejorar también la seguridad.

> Nota: Incluso HTTPS no puede proteger la confidencialidad de los datos en todos los casos. Por ejemplo, las vulnerabilidades CRIME y BEAST permiten a un atacante leer los datos. Además, note que algunas redes utilizan proxys HTTPS MITM para auditorías. Los atacantes también pueden establecer este tipo de proxy.

### Gestión de sesiones no adaptativas

El gestor de sesiones de PHP es adaptativo, por defecto. Un gestor de sesiones adaptativo introduce riesgos adicionales.

Cuando session.use_strict_mode está activado, y el gestor de guardado de sesiones lo soporta, un identificador de sesión no inicializado es rechazado, y uno nuevo es creado. Esto previene un ataque que fuerza a los usuarios a utilizar un identificador de sesión conocido. Un atacante puede pasar enlaces o enviar emails que contienen el identificador de sesión. Por ejemplo: `http://example.com/page.php?PHPSESSID=123456789` si session.use_trans_sid está activado, la víctima iniciará una sesión utilizando el identificador de sesión proporcionado por el atacante. session.use_strict_mode permite anular este tipo de riesgo.

Advertencia

Los gestores de guardado definidos por el usuario también pueden soportar el modo de sesión estricto implementando la validación de identificadores de sesión. Todos los gestores de guardado definidos por el usuario deben implementar la validación de identificadores de sesión.

La cookie de identificador de sesión puede ser definida con los atributos domain, path, httponly, secure y, desde PHP 7.3, SameSite. Existe una prioridad definida por los navegadores. Utilizando las prioridades, un atacante puede definir el identificador de sesión que puede ser utilizado permanentemente. El uso de la directiva session.use_only_cookies no permite resolver este problema. session.use_strict_mode permite mitigar este riesgo. Con la directiva session.use_strict_mode=On, el identificador de sesión no inicializado será rechazado.

> Nota: Aunque la directiva session.use_strict_mode limita los riesgos concernientes al gestor adaptativo de sesión, un atacante puede forzar a los usuarios a utilizar un identificador de sesión no inicializado que ha sido creado por el atacante, por ejemplo, mediante inyecciones Javascript. Este tipo de ataque puede ser limitado utilizando las recomendaciones de este manual. Siguiendo este manual, los desarrolladores deben activar la directiva session.use_strict_mode, utilizar timestamps basados en el gestor de sesión, y regenerar los identificadores de sesión utilizando la función session_regenerate_id() con los procedimientos recomendados. Si los desarrolladores siguen estas instrucciones, un identificador de sesión generado por un atacante será normalmente eliminado. Cuando ocurre un acceso a una sesión obsoleta, los desarrolladores deben guardar todas las datos de la sesión activa del usuario; estas informaciones serán útiles para futuras investigaciones. El usuario debe ser forzado a desconectarse de todas las sesiones, por ejemplo, forzándolo a identificarse de nuevo. Esto permite contrarrestar ataques utilizando sesiones robadas.

Advertencia

El acceso a una sesión obsoleta no significa necesariamente que se trate de una ataque. Una red inestable y/o la eliminación inmediata de la sesión activa hará que usuarios legítimos utilicen sesiones obsoletas.

Desde 7.1.0, la función session_create_id() ha sido añadida. Esta función permite acceder a todas las sesiones activas de un usuario prefijando los identificadores de sesión con el identificador del usuario. La activación de la directiva session.use_strict_mode es vital en esta configuración. De lo contrario, los usuarios maliciosos pueden definir identificadores de sesiones para otros usuarios.

> Nota: Los usuarios de versiones anteriores a PHP 7.1.0 deben utilizar CSPRNG, por ejemplo, /dev/urandom, o la función random_bytes() y las funciones de hash para generar un nuevo identificador de sesión. La función session_create_id() posee mecanismos de detección de colisiones, y genera un identificador de sesión siguiendo las configuraciones INI de las sesiones. El uso de la función session_create_id() es recomendado.

### Regeneración de un identificador de sesión

La directiva session.use_strict_mode es un buen compromiso pero no es suficiente. Los desarrolladores deben también utilizar la función session_regenerate_id() para la seguridad de las sesiones.

La regeneración de un identificador de sesión reduce el riesgo de robo de identificadores de sesión, por lo que la función session_regenerate_id() debe ser utilizada periódicamente; por ejemplo, regenerar el identificador de sesión cada 15 minutos para asegurar contenido sensible. Incluso en el caso de que un identificador de sesión sea robado, tanto el usuario legítimo como el atacante tendrán su sesión que expirará. En otras palabras, el acceso al contenido por el usuario o el atacante generará un error de acceso a una sesión obsoleta.

Los identificadores de sesión deben ser regenerados cuando los privilegios del usuario son elevados, como después de una autenticación. La función session_regenerate_id() debe ser llamada antes de almacenar las informaciones de autenticación en $_SESSION. (la función session_regenerate_id() guarda los datos de sesión actuales automáticamente para guardar timestamps/etc... en la sesión actual.) Asegúrese de que la nueva sesión contenga la bandera de autenticación.

Los desarrolladores no deben basarse en la expiración del identificador de sesión definida por la directiva session.gc_maxlifetime. Los atacantes pueden acceder al identificador de sesión de la víctima de forma periódica para evitar su expiración, y permitir su explotación incluyendo con sesiones autenticadas.

En su lugar, los desarrolladores deben implementar un timestamp basado en la gestión de datos de sesión.

Advertencia

Aunque el gestor de sesión puede manejar los timestamps de forma transparente, esta funcionalidad no está implementada. Los datos de las sesiones antiguas deben ser conservados hasta que el recuperador de memoria no haya pasado. Simultáneamente, los desarrolladores deben asegurarse ellos mismos de que los datos de sesión obsoleta sean efectivamente borrados. Sin embargo, los desarrolladores no deben borrar los datos de sesión activa demasiado rápido. Por ejemplo, `session_regenerate_id(true);` y session_destroy() nunca deben ser llamados al mismo tiempo para una sesión activa. Esto puede parecer contradictorio, pero es un requisito del mandante.

session_regenerate_id() no borrará las sesiones antiguas por defecto. Las sesiones autenticadas obsoletas pueden estar presentes para ser utilizadas. Los desarrolladores deben asegurarse de que las sesiones antiguas no sean utilizadas por nadie. Deben prohibir el acceso a los datos de sesión obsoleta utilizando ellos mismos timestamps.

Advertencia

La eliminación repentina de una sesión activa produce efectos secundarios indeseables. Las sesiones pueden desaparecer cuando hay conexiones concurrentes en la aplicación web y/o cuando la red es inestable.

Los accesos potencialmente maliciosos son indetectables con la eliminación repentina de una sesión.

En lugar de eliminar las sesiones obsoletas inmediatamente, los desarrolladores deben definir un corto tiempo de expiración (timestamp) en $_SESSION, y prohibir el acceso a los datos de sesión.

Los desarrolladores no deben prohibir el acceso a los datos de las sesiones antiguas inmediatamente después de la ejecución de la función session_regenerate_id(). El acceso debe ser prohibido en una etapa posterior; por ejemplo, unos segundos después para redes estables, como una red cableada y unos minutos después para redes inestables como teléfonos móviles o redes Wi-Fi.

Si un usuario accede a una sesión obsoleta (sesión expirada), el acceso a esta sesión debe ser rechazado. También es recomendado borrar el estado de autenticación de todas las sesiones de usuario, ya que esto puede representar un eje de ataque.

El uso de la directiva session.use_only_cookies y de la función session_regenerate_id() pueden causar un DoS personal con cookies no eliminadas definidas por los atacantes. En este caso, los desarrolladores pueden invitar a los usuarios a eliminar las cookies y advertirles que pueden encontrar un problema de seguridad. Los atacantes pueden definir cookies maliciosas mediante una aplicación web vulnerable, un plugin de navegador expuesto o viciado, un dispositivo físico comprometido, etc...

Advertencia

No se confunda sobre el riesgo DoS. session.use_strict_mode=On es obligatorio para la seguridad de los identificadores de sesión ! Todos los sitios son animados a activar la directiva session.use_strict_mode.

DoS solo puede ocurrir cuando la cuenta sufre un ataque. Una inyección Javascript en una aplicación representa la mayoría de los ejes de ataque.

### Eliminación de datos de sesión

Los datos de sesiones obsoletas deben ser inaccesibles y deben ser eliminados. El módulo actual de sesión no soporta este aspecto.

Los datos de sesiones obsoletas deben ser eliminados tan pronto como sea posible. Sin embargo, las sesiones activas no deben ser eliminadas instantáneamente. Para satisfacer estas recomendaciones, los desarrolladores mismos deben implementar un gestor de datos de sesión basado en timestamp.

Defina y gestione la expiración del timestamp en la variable global $_SESSION. Prohíba el acceso a los datos de sesiones caducadas. Cuando se detecte un acceso a datos de sesión obsoleta, debe eliminarse todo el estado autenticado de las sesiones de usuario y forzar a los usuarios a autenticarse de nuevo. El acceso a datos de sesiones obsoletas puede representar un ataque. Para lograr esto, los desarrolladores deben seguir todas las sesiones activas de todos los usuarios.

> Nota: El acceso a una sesión obsoleta también puede ocurrir debido a una red inestable y/o un acceso concurrente a un sitio web, por ejemplo, el servidor intenta definir un nuevo identificador de sesión mediante una cookie, pero el paquete Set-Cookie nunca llegó al cliente debido a una pérdida de conexión. Una conexión puede crear un nuevo identificador de sesión mediante la función session_regenerate_id(), pero otra conexión concurrente puede no haber recibido aún el identificador de sesión. Sin embargo, los desarrolladores deben prohibir el acceso a una sesión obsoleta en un momento más lejano. Por ejemplo, la gestión de sesiones basada en timestamp es obligatoria.

En resumen, los datos de sesiones no deben ser destruidos con la función session_regenerate_id(), ni con la función session_destroy(), pero los timestamps deben ser utilizados para controlar el acceso a los datos de sesión. Deje que la función session_gc() elimine los datos obsoletos desde el almacenamiento de datos de sesiones.

### Sesión y Bloqueo

Los datos de sesión están bloqueados por defecto para evitar los accesos concurrentes. El bloqueo es obligatorio para mantener una consistencia de los datos de sesión a través de las peticiones.

Sin embargo, el bloqueo de sesión puede ser utilizado por los atacantes para realizar ataques DoS. Para minimizar el riesgo de un ataque DoS por bloqueo de sesión, debe minimizarse el uso de bloqueos. Utilice datos en modo solo lectura cuando los datos de sesión no necesiten ser actualizados. Utilice la opción 'read_and_close' con la función session_start(). `session_start(['read_and_close'=>1]);` cerrará la sesión tan pronto como sea posible después de actualizar la variable global $_SESSION utilizando la función session_commit().

El módulo de sesión actual no detecta todas las modificaciones de la variable $_SESSION cuando la sesión está inactiva. Es responsabilidad del desarrollador no modificar la variable $_SESSION cuando la sesión está inactiva.

### Sesiones activas

Los desarrolladores deben mantener un registro de todas las sesiones activas de cada usuario, y notificarles el número de sesiones activas, desde qué dirección IP, desde cuándo, etc. PHP no mantiene registros de estas informaciones. Los desarrolladores están supuestos a hacerlo ellos mismos.

Existen diferentes formas de hacerlo. Una implementación posible es definir una base de datos que mantenga un registro de los datos necesarios, y almacenar todas las informaciones pertinentes. Dado que los datos de sesión son GCed, los desarrolladores deben tener cuidado con los datos GCed para mantener la base de datos de sesiones activas consistente.

Una de las implementaciones simples es "el identificador de usuario prefijando el identificador de sesión" y almacenar las informaciones necesarias en la variable $_SESSION. La mayoría de las bases de datos son relativamente eficientes para seleccionar un prefijo en forma de string. Los desarrolladores DEBEN utilizar la función session_regenerate_id() así como la función session_create_id() para esto.

Advertencia

Nunca utilice datos confidenciales como prefijo. Si el identificador de usuario es confidencial, debería utilizar la función hash_hmac().

Advertencia

La activación de la directiva session.use_strict_mode es obligatoria para este tipo de configuración. Asegúrese de que esté activada. De lo contrario, la base de datos de sesiones activas puede ser comprometida.

El gestor de sesión basado en timestamp es obligatorio para detectar el acceso a sesiones obsoletas. Cuando se detecte el acceso a una sesión obsoleta, la bandera de autenticación debe ser eliminada de todas las sesiones activas del usuario. Esto permite evitar que los atacantes continúen explotando las sesiones robadas.

### Sesión y auto-identificación

Los desarrolladores no deben utilizar identificadores de sesión con una larga duración para la auto-identificación, ya que esto aumenta el riesgo de utilizar sesiones robadas. Una funcionalidad de auto-identificación debe ser implementada por el desarrollador.

Utilice una clave de hash segura de un solo uso como clave de auto-identificación utilizando la función setcookie(). Utilice un hash seguro más fuerte que SHA-2. Por ejemplo, SHA-256 o superior con datos aleatorios desde la función random_bytes() o mediante /dev/urandom.

Si el usuario no está autenticado, verifique si la clave de auto-identificación de un solo uso es válida o no. En el caso de que sea válida, autentifique al usuario y defina una nueva clave de hash segura de un solo uso. Una clave de auto-identificación solo debe ser utilizada una vez, por ejemplo, nunca utilice una clave de auto-identificación, y siempre regénela.

Una clave de auto-identificación es una clave de autenticación con una larga duración, debe ser protegida tanto como sea posible. Utilice los atributos de cookie path/httponly/secure/SameSite para protegerla. Por ejemplo, nunca transmita la clave de auto-identificación a menos que sea necesario.

Los desarrolladores deben implementar las funcionalidades que desactivan la auto-identificación, y eliminan las cookies que contienen las claves de auto-identificación no necesarias.

### Ataques CSRF (Cross-Site Request Forgeries)

Las sesiones y las autenticaciones no protegen contra los ataques CSRF. Los desarrolladores deben implementar protecciones CSRF ellos mismos.

La función output_add_rewrite_var() puede ser utilizada para la protección CSRF. Consulte las páginas del manual para más detalles.

> Nota: PHP, antes de su versión 7.2.0, utiliza el mismo buffer de salida y las mismas configuraciones INI que la configuración trans-sid. Sin embargo, el uso de la función output_add_rewrite_var() con versiones de PHP anteriores a 7.2.0 no es recomendado.

La mayoría de los frameworks de aplicaciones web soportan la protección CSRF. Consulte el manual de su framework de aplicación web para más detalles.

Desde PHP 7.3, el atributo SameSite de la cookie de sesión puede ser definido. Esto es una medida adicional que puede minimizar las vulnerabilidades CSRF.

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open