Desde hace un tiempo que estaba teniendo un problema en mi aplicación en una zona restringida para los administradores y redactores de la web (no se trata de este blog).
Los administradores y los redactores accedían a esta sección de la web para gestionar toda la aplicación, así como crear contenido, etc,… el problema es que tras navegar por la aplicación interna, realizar diferentes acciones, crear contenido, editar o lo que necesitaran hacer llegaba un momento que la aplicación se deslogueaba. Porqué?
El problema reside en la session que se crea bajo HTTP o HTTPS no son iguales.
De manera que una session creada bajo HTTP como la siguiente
[code lang=»PHP»]
<?php $_SESSION[‘user’] = ‘pedro’; ?>
[/code]
bajo HTTPS si queremos hacer un print_r o un var_dump, no tiene valor, no existe.
[code lang=»PHP»]
<?php var_dump($_SESSION[‘user’]); ?>
[/code]
Sessión creada bajo HTTP creada con CakePHP
Ahora bien, porque se deslogueaba?
Normalmente los paneles de control o herramientas de gestión internas y privadas, la cuenta de facebook, etc,.. tienen el login inicial y sucesivas comprobaciones de las credenciales a lo largo de la conexión por medio de la sessión. Pues bién en mi caso había ciertas llamadas bajo http o algunos elementos que incluía por ejemplo un .css o un .js que no existían y daban como resultado un error 404 pero bajo http, lo que pasaba es que la conexión http me estaba sobrescribiendo la session de https y cuando el panel de control interno quería comprobar de nuevo las credenciales mediante la sessión, esta se había sobrescrito y estaba vacía de manera que como sistema de seguridad se deslogueaba.
Solución 1. La más rápida
La solución más sencilla a priori es obtener el id de session que se asigna mediante session_start() y pasarla por url. Ejemplo http://www.example.com/index.php?sid=123 => https://secure.example.com/login.php?sid=123
Esto no es ni siquiera recomendable. Lo comento porque lo he visto en muchos blogs y foros. Y personalmente no enviaría el id de session por url, porque puede dar lugar a ataques de tipo Session fixation. (Más sobre Session Fixation Vulnerability). A parte parece una chapuza o un parche, y si parece una chapuza entonces es que lo es.
Creo que nunca lo he hecho hasta el momento y es posible que no lo haga.
Solución 2. Usar sólo cookies bajo HTTPS
- Cuando usas session_start() se genera una cookie con el ID de session y se almacena en el navegador del usuario. La session ID es solicitada, por lo que el script PHP puede identificar la session del usuario.
- Se puede establecer la configuración de la session con parámetros en el PHP para restringir la cookie de session para que solo se solicite en peticiones bajo HTTPS. Esto hace que el ID de session se envíe encriptado, pero que no funcione la session en peticiones bajo HTTP. Lo que tampoco es algo productivo.
Para ello tendremos que añadir una directiva de PHP al código.
session.cookie_secure especifica si las cookies deberían enviarse sólo sobre conexiones seguras. Por defecto es off.
[code lang=»PHP»]
if (env(‘HTTPS’)) {
ini_set(‘session.cookie_secure’, 1);
}
[/code]
De esta manera forzamos a que las cookies se envíen siempre encriptadas.
O para forzar que sea en toda la aplicación, habría que poner la directiva session.cooke_secure a 1 en cualquier archivo de configuración principal para que herede toda la aplicación.
Solución 3. Usar sólo cookies bajo HTTP
Sería el caso contrario al anterior, el problema es que el id de session no estaría encriptado y sería un nivel que quitamos de seguridad, a la aplicación.
Bastaría con forzar el mismo parámetro anterior a 0.
[code lang=»PHP»]
ini_set(‘session.cookie_secure’, 0);
[/code]
Como yo uso mucho el framework CakePHP también he querido probar esta solución.
Habría que comentar la linea 420 en /cake/lib/session.php, la linea sería la siguiente:ini_set(‘session.cookie_secure’, 1);
[code lang=»PHP»]
if (env(‘HTTPS’)) {
ini_set(‘session.cookie_secure’, 1);
}
[/code]
Una solución mejor
Quizá la mejor solución es manejar sessiones bajo HTTP y HTTPS pero para eso se requiere que el login, paneles de control de usuario y demás información personal se lleve a cabo bajo HTTPS de manera que toda la información sensible esté en la cookie encriptada y usar una session PHP normal para el resto de datos en general, sin restricciones ni seguridad.
De entre todo el código que he hecho y que he visto, podría como posibles soluciones las siguientes.
[code lang=»PHP»]
session_start();
setcookie(session_name(), session_id(), NULL, NULL, NULL, 0);
setcookie(session_name(), session_id(), NULL, NULL, NULL, 1);
[/code]
Con lo cual creamos dos cookies de sessiones una para HTTP y otra para HTTPS
[code lang=»PHP»]
<?php
$secure = array_key_exists(‘HTTPS’, $_SERVER);
$cookie = false;
// existing session
if(array_key_exists(‘PHPSESSID’, $_COOKIE))
{
$cookie = setcookie(‘PHPSESSID’, $_COOKIE[‘PHPSESSID’], NULL, NULL, NULL, (int) !$secure);
}
session_start();
// new session
if(!$cookie)
{
setcookie(session_name(), session_id(), NULL, NULL, NULL, (int) !$secure);
}
?>
[/code]
De todas maneras este artículo está abierto a sugerencias y comentarios, no estaría mal saber como otros desarrolladores gestionan este problema.
Hola, y enhorabuena por tu blog. Estoy trabajando en un proyecto con otro compañero; él con php puro y yo con el framework CakePHP, y desde una aplicación linca con la otra, y viceversa. El problema es que no soy capaz de leer las variables de sesión credas en php desde CakePHP, ¿alguna idea?
Antes que nada gracias muy interesante, por cierto yo tengo un problema medio raro que es cuando pongo http:// o http://www. no se pasan las sesiones no se si me puedas ayudar
La forma en como lo solucionamos un amigo y yo, es que en el hosting, se genere un redireccionamiento de direcciones que tengan «www.» o cualquier otro prefijo a la dirección https://mipagina.com, saludos!