El término inglés cookie se traduce literalmente por galleta. Fueron introducidas por Lou Montulli en las primeras versiones del navegador Netscape. La razón de su existencia se debe a que el protocolo HTTP no está preparado para el uso de estados. Un estado en informática se define como una configuración de un sistema en un momento dado. Esa configuración viene a ser información que puede almacenarse en algún lugar para uso futuro. Así cuando se cierra una página web no queda ningún rastro de ese proceso. En definitiva las solicitudes HTTP realizadas al servidor son totalmente independientes.

Pero a veces es necesario almacenar ese estado. Por ejemplo, si en nuestro sitio queremos controlar el acceso de usuarios a ciertas páginas, hemos de tener en el servidor una lista de usuarios registrados. Pero en el navegador debe guardarse también la información de registro, pues de otra forma el usuario tiene que identificarse cada vez que pase de una página a otra.

Una cookie es un trozo de texto que un servidor web almacena en el disco duro del ordenador del usuario para recuperarla cuando la necesite. El contenido son parejas de nombre y valor que pueden usarse para almacenar el estado del sistema. Es importante señalar que sólo puede almacenarse texto y con ciertas limitaciones. Por si mismas las cookies no hacen nada, es decir, no son programas ni ninguna otra cosa que puede ejecutar algo no deseado.

En la medida de lo posible hay que buscar los términos en español, pero no podemos traducir cookie directamente por galleta en este contexto. Se puede traducir por su definición como registro de identificación o similar, pero quizás es más apropiado traducirlo como huella tal como hace el navegador Opera. Realmente son huellas que el servidor deja en el navegador para poder identificarlo. De todas formas el término en inglés está tan difundido que quizás es mejor usarlo sin traducir.

Como mucha documentación está en inglés, es conveniente conocer estas definiciones:

State (estado):
En sistemas que procesan la información, un estado es un conjunto de propiedades transmitidas por un objeto a un observador a través de algún canal. Aplicado a HTTP, las propiedades serán transmitidas por el navegador al servidor, de tal forma que éste determina que información servir en base a las propiedades de ese estado.
Stateless (sin estado), stateful (con estado)
Son adjetivos que se forman con state less y state full. Significa que un sistema de información trabaja sin o con conocimiento del estado del mismo, respectivamente. HTTP es un sistema stateless (sin manejo de estados), pero con el uso de cookies se convierte en un sistema stateful (con manejo de estados).
Session (sesión)
Una sesión (de comunicación) es un intercambio no permanente de información entre sistemas. Haciendo HTTP con estados mediante el uso de las cookies, una sesión es el proceso de comunicación entre servidor y navegador para intercambiar la información de estado. Es clásico el ejemplo de una tienda web (el "shopping cart"), donde el usuario selecciona productos que va agregando a su "carrito" a medida que navega entre las páginas del sitio. Las sesiones se caracterizan por:
  • Cada sesión tiene un inicio y un final.
  • Las sesiones no son permanentes.
  • Una sesión puede ser finalizada por el servidor o por el navegador.
  • Una sesión siempre está implícita cuando se intercambia información de estado.
El concepto de sesión es independiente de las cookies, por lo que puede haber sesiones que se manejen sin ellas, tal como veremos más adelante. Sin embargo una cookie está normalmente relacionada con una sesión.

Los documentos que sirven de estándar para implantar HTTP con estados son los siguientes:

  • RFC2109 del año 1997 donde se describe inicialmente un protocolo para manejos de estado en HTTP (HTTP State Management Mechanism)
  • RFC2965 del año 2000, una revisión y mejora del anterior.

Literalmente el objetivo de estos documentos es especificar una forma para crear un sesión con estado en solicitudes y respuestas con el protocolo HTTP.

El proceso de una sesión PHP

Tal como señala el manual de PHP sobre sesiones, el concepto es preservar cierta información a través de accesos subsiguientes de los usuarios. El proceso en PHP es el siguiente:

  1. Un usuario entra por primera vez en una página del sitio que usa sesiones para, a partir de esa página, navegar a otras.
  2. PHP crea un identificador único para esa sesión (designado para abreviar a veces como ID). Digamos de forma sencilla que es una cadena de números y letras creada por PHP que identifica de forma única un usuario en un momento del tiempo. Esta cadena se genera de forma aleatoria y se almacena en el servidor, estando disponible en el navegador del usuario de dos formas posibles:
    • El servidor envía una cookie al navegador del usuario con el identificador. Podemos llamarlas sesiones basadas en cookies.
    • El servidor propaga el identificador a través de la URL. En este caso podemos denominarlas sesiones basadas en URL.
  3. Después de recibir la primera respuesta desde el servidor acompañada del identificador, el navegador hace las siguientes peticiones de otras páginas acompañando el identificador, tanto como cookie o por la URL. Así el servidor identifica al navegador.
Aparte de usar cookies o enviar por la URL el identificador de sesión, podría haber otra forma usando un formulario con un campo de texto oculto. En el tema V sobre propagar sesiones veremos ejemplos de las distintas formas para propagar el identificador de sesión.

Según se desprende del manual PHP, las sesiones basadas en cookies son más seguras que las basadas en URL. Por defecto en el archivo de configuración php.ini (versión 5.2.13) encontramos los siguientes valores para configuraciones de sesiones que pueden ser modificadas en tiempo de ejecución:

La opción session.use_cookies, que especifica si PHP usará cookies para propagar las sesiones. Por defecto se establece en 1 (true), tal como se ve en el php.ini:

; Whether to use cookies.
; http://php.net/session.use-cookies
session.use_cookies = 1

La opción session.use_only_cookies, que especifica si PHP sólo usará cookies para almacenar el identificador de sesión en el navegador del cliente, no permitiendo la otra opción de propagarlo por la URL. El valor predeterminado es 1 (true), recomendando usar cookies para evitar la suplantación de sesiones (hijacking), al menos en parte. Esto es lo que aparece en el php.ini:

; This option forces PHP to fetch and use a cookie for storing and maintaining
; the session id. We encourage this operation as it's very helpful in combatting
; session hijacking when not specifying and managing your own session id. It is
; not the end all be all of session hijacking defense, but it's a good start.
; http://php.net/session.use-only-cookies
session.use_only_cookies = 1

La opción session.use_trans_sid, que especifica que el identificador sea comunicado a través de URL entre el navegador y el usuario de forma transparente, es decir sin necesidad de incorporar el identificador en cada petición o respuesta. Por defecto es 0 (false). SID es una constante de PHP que contiene el nombre de la sesión y el identificador en la forma "name=ID" o una cadena vacía si se está usando la opción de sesiones basadas en cookies. Para usar sesiones no basadas en cookies sino en URL debemos primero poner la opción session.use_cookies = 0 y luego esta opción a 1. Sin embargo debe tenerse en cuenta el riesgo asumido, tal como se expone en el php.ini:

; trans sid support is disabled by default.
; Use of trans sid may risk your users security.
; Use this option with caution.
; - User may send URL contains active session ID
;   to other person via. email/irc/etc.
; - URL that contains active session ID may be stored
;   in publically accessible computer.
; - User may access your site with the same session ID
;   always using URL stored in browser's history or bookmarks.
; http://php.net/session.use-trans-sid
session.use_trans_sid = 0
Algunas configuraciones pueden modificarse en tiempo de ejecución con la función ini_set(a, b), donde "a" será el string de la variable de configuración y "b" será el nuevo valor a poner. Por ejemplo, para habilitar sesiones basadas en URL haríamos:
ini_set("session.use_cookies", 0);
ini_set("session.use_trans_sid", 1);
session_start();

Traducción del comentario de esta opción:
El uso de trans sid puede poner en riesgo la seguridad de los usuarios. Use esta opción con precaución.

  • Un usuario podría enviar una URL conteniendo un identificador de una sesión activa de otro usuario a través de un email, canales IRC, etc.
  • Una URL con una sesión activa podría ser almacenada en ordenadores públicamente accesibles.
  • Un usuario podría acceder con el mismo identificador de otro usuario utilizando una URL almacenada en el historial del navegador.

Si PHP recomienda usar sesiones basadas en cookies tendremos que hacerle caso, pues ahora mismo no se mucho más del tema. Pero hay que tener en cuenta que en este caso el navegador tiene que aceptar las cookies, pues el usuario puede tenerlas desactivadas. En cuanto a esto, el estándar RFC2965 sobre el mecanismo de estado de sesiones basadas en cookies, en su punto 2 expone literalmente:

"Neither clients nor servers are required to support cookies. A server MAY refuse to provide content to a client that does not return the cookies it sends.": Ni navegador ni servidor deben estar obligados a soportar cookies. Un servidor podría rehusar servir contenido a un navegador que no le devuelve una cookie que previamente le fue enviada.

Por lo tanto, y para empezar, suponemos que el servidor manejará sesiones basadas en cookies que deberán ser aceptadas por el navegador. En el tema V sobre propagar sesiones veremos con ejemplos otras formas de propagar las sesiones para los casos en que las cookies no estuvieran activadas en el navegador.

Ejemplo de una sesión PHP basada en cookies

Antes de meternos en un ejemplo para ver cómo trabajan una sesión, no debemos olvidar que las sesiones sirven para mantener información entre accesos a distintas páginas. Esa información, que se almacena en el servidor, estará disponible a través de las variables de sesión pero no de forma permanente. Digo esto porque yo mismo pensaba que las sesiones son para eso del "registro y acceso de usuarios", conocido como proceso de autenticación por el cual un sistema verifica que un usuario es quién dice ser, para entonces permitir o denegar el acceso. Las sesiones sirven para esto, pero su principal objetivo es mantener la información entre accesos.

Supongamos que tenemos un catálogo de productos en un sitio. Se compone de un conjunto grande de páginas, cada página para una línea de productos, que a su vez se desglosa en subpáginas con sublíneas. Nos podría interesar ofrecer al usuario un sistema que le ayude a encontrar lo que necesita. Por ejemplo, si en la primera página elige la línea "electrodomésticos" y en la siguiente "frigoríficos", podríamos ofrecerle todos los productos relacionados en esa sublínea y además un conjunto de enlaces relacionados. Algo así como "quizás le interese también congeladores, hornos, lavadoras, ...". Pero si elige "televisores" podríamos sugerirle "cámaras de video, aparatos música, ...".

Ese es un ejemplo de personalización de páginas. Para que un sistema así funcione es necesario almacenar la información previa del camino que va siguiendo el usuario. Para esto pueden servir las sesiones, pero no es estrictamente necesario que el usuario esté registrado en alguna base de datos.

Usaremos un ejemplo más simple. Sea una primera página (pagina1.php) que abre una sesión para navegar por un conjunto siguiente de páginas (pagina2.php y otras). Supongamos que deseamos abrir la sesión con objeto de personalizar el conjunto de páginas segunda y siguientes. Para simplificar el ejemplo hacemos que la personalización sea sólo para el color del fondo de las páginas. Podemos ya entrar en la pagina1.php que tiene el siguiente código (obviamos con puntos suspensivos el HTML que no es de interés):

<?php
//Lo primero es iniciar la sesión antes de enviar nada, pero
//estableciendo el modo con cookies por si estuviera desactivado
ini_set("session.use_cookies", 1);
ini_set("session.use_only_cookies", 1);
session_start();
//Por defecto asignamos un color para el fondo. Si esta es la
//primera vez que abrimos la sesión, le pone el color amarillo.
if (!isset($_SESSION['color-fondo'])) {
    //Esto evita la fijación de sesión
    session_regenerate_id(true);
    //Ahora asignamos el parámetro de sesión por primera vez
    $_SESSION['color-fondo']  = 'yellow';
}
//Ahora se construye la página
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
    ...
    <h3>Página 1</h3>
    <p>Esta página ... el color del fondo, que ahora tiene el valor
    <code><?php echo htmlspecialchars($_SESSION['color-fondo']); ?></code>,
    color de fondo que se pondrá en las páginas 2 y 3.
    </p>
    <p>Podemos ver la cookie que almacenó PHP en el navegador:<br />
    <code id="mi-cookie"></code>
    </p>
    <script>
        var arrayCookies = document.cookie.split(";");
        for (var i=0; i<arrayCookies.length; i++){
            var unaCookie = arrayCookies[i].split("=");
            var nombre = quitaEspacios(unaCookie[0]);
            if (nombre == "PHPSESSID") {
               var contenido = escapaHtml(arrayCookies[i]);
               setInnerText(document.getElementById("mi-cookie"), contenido);
               break;
            }
        }
    </script>    
    ...
    

En color marrón está el código PHP y en azul el HTML, incluso el de JavaScript. Los comentarios están en verde. En primer lugar vemos que para iniciar una página con sesión hemos de reconfigurar las opciones de inicio session.use_cookies y session.use_only_cookies para que la sesión sólo use cookies. Es una medida extra de seguridad por si la configuración se hubiera modificado, pues en otro caso se usaría URL para enviar la sesión y esta es una vía que no se aconseja como vimos más arriba. Luego hacemos session_start() antes que se envíe cualquier cosa al navegador. Con esto se inicia la sesión pero no la consideramos activa hasta que guardemos algún parámetro de la sesión como $_SESSION['color-fondo'] = 'yellow'. Para saber si ya la sesión fue iniciada antes miramos con isset() si nuestro parámetro color-fondo está ya registrado. Si no lo estuviera es que es el primer inicio de la sesión y lo registramos con un color de fondo amarillo, por ejemplo.

Entonces el servidor creará un identificador único para esa sesión. Este identificador se mantiene mientras mantengamos abierto el navegador, por lo que cada vez que accedamos a las páginas del ejemplo veremos que se mantiene el color de fondo seleccionado. Este ejemplo se ejecuta en mi servidor Apache + PHP montado en local sólo para aprender. Puede ver más detalles de la instalación en Apache 2.2.15 y PHP 5.2.13 en Windows. Buscando el php.ini encontramos una línea con session.save_path="C:\WINDOWS\Temp", que es el lugar donde se configura para guardar las sesiones. Esta es una carpeta que usa el servidor para almacenar la sesión, cuyo contenido después de ejecutar la primera página es el siguiente:

Carpeta temp del servidor

Crea un archivo con el nombre iniciado con sess_q75n4oq6turdv67uohqkbl7lv5. La ristra después de sess_ es el identificador único de esa sesión. Si abrimos el archivo con un bloc de notas podemos ver que contiene color-fondo|s:6:"yellow";. Se trata del parámetro que le pasamos con el color de fondo. Realmente no tenemos que preocuparnos por esto pues nos basta con crear nuevos parámetros para una sesión simplemente haciendo $_SESSION["parametro"] = $algun_valor. Para recuperar un parámetro bastaría hacer $variable = $_SESSION["parametro"]. (La barra vertical separa el nombre del valor del parámetro y que la s:6: significa que el valor es un string con 6 caracteres).

A continuación el servidor crea el documento pagina1.php que será enviado al navegador del usuario. Lo que se envía incluyendo la cabecera HTTP será algo como esto:

HTTP/1.1 200 OK
Date: Sun, 12 Sep 2010 22:46:25 GMT
Server: Apache/2.2.15 (Win32) PHP/5.2.13
X-Powered-By: PHP/5.2.13
Set-Cookie: PHPSESSID=q75n4oq6turdv67uohqkbl7lv5; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 2031
Content-Type: text/html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
<head>
    <title>Página 1</title>
    ... continúa el resto de la página ...

Esto lo he obtenido mediante un script PHP que hace algo parecido al Telnet en Windows. Puede ver más detalles en telnet con php. En todo caso lo que nos interesa apreciar es que el servidor envia una orden para que el navegador albergue una cookie. Ésta contiene el identificador de la sesión, el mismo que almacena en la carpeta de sesiones C:/Windows/Temp como vimos antes. El nombre dado a la cookie siempre es PHPSESSID. Este nombre se configura en el php.ini inicialmente por defecto (aunque puede modificarse):

; Name of the session (used as cookie name).
; http://php.net/session.name
session.name = PHPSESSID    
    

Un detalle interesante es la fecha de expiración de la cookie. Se observa una fecha anterior, el 19/11/1981, con lo que la cookie que se almacena en el navegador tiene una duración de vida mientras el navegador esté abierto. Por lo tanto sólo va a existir en memoria y no será almacenada en el disco duro. En ese caso y con Windows se grabarían en las carpetas de documentos y configuración, en algún lugar de la carpeta del usuario. Pero creo que no viene al caso saber donde se almacenan, pues todos los navegadores nos dan la posibilidad de consultarlas y, en algún caso, hasta modificarlas.

Podemos ver las cookies en Internet Explorer 8 mediante herramientas de desarrollo, luego en caché y en ver información de cookies. Este navegador construye una página para mostrar todas las cookies almacenadas, por lo que podemos buscar al final del documento, apareciendo algo como esta imagen, donde el identificador es igual que el primero de la lista de la imagen de más arriba:

Cookie IE

Es importante ver que Internet Explorer no muestra el dominio del que procede cuando estamos trabajando con localhost, pero que debe ser algún error puesto que para el resto de cookies si se ve el dominio. De todas formas aprovechamos para decir que las cookies de un dominio sólo pueden ser gestionadas por ese dominio, por lo que es un dato de suma importancia que se almacena con la cookie. Observe el tiempo de expiración at the end of the session, al finalizar la sesión. En los siguientes navegadores veremos que se aporta más información.

En Opera 10.61 podemos verlas en herramientas, avanzados, huellas(cookies) con algo como esto. Este navegador es el que me parece más completo al mostrar las cookies (huellas como le llama), pues ofrece además la posibilidad de editarlas. También se puede acceder con el botón derecho del ratón con la opción editar opciones del sitio, huellas, mostrando todas las cookies del dominio de esa página. Se corresponde con el segundo identificador de la lista mencionada.

Cookie Opera

En Firefox 3.6.8 podemos verlas en opciones, privacidad, mostrar cookies con algo como esto, por supuesto con otro identificador de sesión. También podemos verlas con el botón derecho del ratón en la página, con la opción ver información de la página y luego en seguridad, obteniéndose todas las cookies para el dominio de esa página. El identificador es el mismo que el tercero de la lista del /Temp en el servidor:

Cookie Firefox

En Safari 4.0.5 están las cookies en el botón de ajustes generales, en la opción de preferencias, seguridad. El identificador es el mismo que el último de la lista en el /Temp del servidor:

Cookie Safari

En cualquier caso la cookie con el identificador de la sesión se almacena en el navegador del usuario. A partir de aquí y siempre dentro de la duración de la sesión, el navegador acompañará esta cookie con las solicitudes de páginas que se hagan a ese mismo servidor. Éste comprueba que el identificador es igual que el que tiene almacenado en la carpeta de sesiones del servidor (en C:/Windows/Temp para mi servidor local), en cuyo caso pone a disposición de uso los parámetros allí almacenados (el color de fondo para nuestro ejemplo).

La importancia de guardar sólo un identificador en el navegador del usuario radica en que es un dato sin valor en sí mismo. Veámos, ¿porqué no guardamos también los parámetros como el color de nuestro ejemplo en la cookie?. Existe la función setcookie() en PHP que permite enviar cookies desde el servidor al navegador. Mientras que con la variable superglobal $_COOKIE podemos verla en el servidor. Podemos argumentar que así no estamos almacenando nada en el servidor. ¿Pero si en lugar de ser un simple color para el fondo de las páginas fuera un dato personal que consideramos privado?. Dónde es más seguro tenerlo, ¿en el servidor o en el navegador?. Por ahora respondemos que en el servidor, siguiendo el criterio general de PHP que es almacenar el identificador en el navegador y el resto de la información en el servidor. Espero que a medida que desarrolle estos temas pueda dar más luz sobre este asunto.

Javascript también puede manejar cookies

En el ejemplo del código de la pagina1.php anterior teníamos un trozo de código en Javascript que reproducimos otra vez:

...
<code id="mi-cookie"></code>
</p>
<script>
    var arrayCookies = document.cookie.split(";");
    for (var i=0; i<arrayCookies.length; i++){
        var unaCookie = arrayCookies[i].split("=");
        var nombre = quitaEspacios(unaCookie[0]);
        if (nombre == "PHPSESSID") {
           var contenido = escapaHtml(arrayCookies[i]);
           setInnerText(document.getElementById("mi-cookie"), contenido);
           break;
        }
    }
</script>  
...  

Podemos ver que usamos document.cookie que nos devuelve un string con todas las cookies del dominio de esa página almacenadas en el navegador. La colección de cookies de un dominio son parejas nombre=valor separadas por punto y coma (;). Así haciendo un split(";") sobre ese string obtenemos un array de parejas nombre=valor, que luego separamos otra vez para obtener el nombre y buscar el que nos interesa, PHPSESSSID para mostrar el identificador en la página de ejemplo. Así veremos que realmente el identificador se almacenó en una cookie de nuestro navegador.

Las tres funciones señaladas quitaEspacios(), escapaHtml() y setInnerText() las he creado para anular espacios antes y después de una cadena, escapar caracteres reservados e insertar texto dentro de un elemento.

No quiero extenderme ahora mucho sobre el manejo de cookies con JavaScript, pues mi interés está ahora en las que se gestionan desde un servidor para el uso de sesiones basadas en cookies. La única razón de exponerlo es para corroborar que esa cookie de sesión está realmente alojada en nuestro navegador.

Gestionando una sesión abierta

A partir de la página 1 del ejemplo podemos acceder a las siguientes páginas, pagina2.php y pagina3.php, ambas de igual estructura. Una vez que pasemos por la página 1 y mientras no cerremos el navegador, la cookie seguirá almacenada en el navegador, por lo que aunque cerremos esa página 1 y mientras el navegador esté abierto, la sesión seguirá viva. Bueno a excepción de que no se destruya si acaba su duración de vida como veremos posteriormente. El código de las páginas 2 y 3 es el siguiente (son iguales), donde ponemos puntos suspensivos en el HTML que no es de interés:

<?php
//Lo primero es iniciar la sesión antes de enviar nada, pero
//estableciendo el modo con cookies por si estuviera desactivado
ini_set("session.use_cookies", 1);
ini_set("session.use_only_cookies", 1);
session_start();
//Establecemos una variable para el color del fondo por defecto
$un_color_fondo = "white";
//Comprobamos si la sesión ya fue iniciada
if (!isset($_SESSION["color-fondo"])){
    //Para evitar la fijación de sesión regeneramos el id
    //cuando la sesión no haya sido iniciada por el cauce previsto
    session_regenerate_id(true);
    //Con sesiones no iniciadas ponemos un color rojo
    $un_color_fondo = "red";
} else {
    //Aunque un parámetro de sesión proviene de una carpeta del
    //servidor, no está de más filtrar caracteres no deseados,
    //pues este color se insertará en el style del body.
    $un_color_fondo = htmlspecialchars($_SESSION["color-fondo"], ENT_QUOTES);
    //Si está página se está recibiendo por la ejecución del botón
    //submit del formulario, tomamos el color seleccionado
    if (isset($_POST["color"])){
        //Filtramos caracteres no deseados
        $un_color_fondo = htmlspecialchars($_POST["color"], ENT_QUOTES);
        //Eliminamos comillas por si el servidor tiene activado el
        //escape con barras invertidas
        if (get_magic_quotes_gpc() == 1) $un_color_fondo = stripslashes($un_color_fondo);
        //Por último actualizamos el parámetro de la sesión con el nuevo color
        $_SESSION["color-fondo"] = $un_color_fondo;
    }    
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
...
<!-- Insertamos en el body el color de fondo -->
<body style="background-color:<?php echo $un_color_fondo; ?>;" >
    ...    
    <h3>Página 2</h3>
    <p>Esta página2 tiene el valor
    <big><big><?php echo $un_color_fondo;?></big></big>
    para la propiedad <code>background-color</code> del cuerpo del documento.
    </p>
    <?php if($un_color_fondo == "red"){?>
        <p style="font-size: 2em;">Ha llegado a esta página sin antes haber pasado
        por la página 1.</p>
    <?php } else {?>
        <!-- Este formulario sirve para que el usuario seleccione otro color para
        el fondo. Al enviarlo se recoge en esta misma página, pues en el inicio
        hemos puesto el script para ello. -->
        <form action="pagina2.php" method="post">
            <label>Seleccione color para el fondo:
                <select name="color">
                    <option value="yellow"
                    <?php if ($un_color_fondo == "yellow") echo "selected='selected'" ?>
                    >amarillo (yellow)</option>
                    <option value="aqua"
                    <?php if ($un_color_fondo == "aqua") echo "selected='selected'" ?>
                    >azul (aqua)</option>
                    <option value="lime"
                    <?php if ($un_color_fondo == "lime") echo "selected='selected'" ?>
                    >verde (lime)</option>
                </select>
            </label>
            <input type="submit" value="enviar" />
        </form>        
    <?php } ?>
    ...(aquí va el JavaScript como el de pagina1.php)...

Recordamos que el proceso para este ejemplo es que el usuario entrase a través de la primera página (pagina1.php), de tal forma que allí se creaba una nueva sesión guardando por primera vez el parámetro $_SESSION['color-fondo'] = 'yellow' si la sesión no hubiese estado iniciada. Recordamos que una sesión se mantiene abierta mientras no se cierre el navegador. Al pasar a la segunda página con el enlace dispuesto en la primera página, lo primero que hacemos es iniciar sesión otra vez con session_start(). Luego hacemos una comprobación de seguridad para ver si el parámetro existe con !isset($_SESSION["color-fondo"]). Lo que se espera es que exista, pues habiendo estado antes (en algún momento) en la primera página tiene que haber sido creado ya el color de fondo. ¿Qué razones hay para que no exista?. Una razón podría ser intentar acceder a pagina2.php sin antes haber estado en la primera página.

Las sesiones no son permanentes, por lo que tienen un inicio y un final. Una forma de finalizar una sesión es cerrar el navegador, pero con esta opción no podríamos acceder directamente a la página 2 con enlace desde la 1, aunque si podríamos acceder poniendo en la barra de direcciones del navegador la URL completa de la página 2. Si cerramos el navegador para que no haya sesión abierta y luego lo abrimos poniendo la URL de pagina2.php en la barra de direcciones del navegador, comprobaremos que el script PHP detecta que no existe la sesión, es decir, el parámetro color de fondo para esa sesión usando if (!isset($_SESSION["color-fondo"])). Si es así le ponemos el color de fondo rojo. Además regeneramos el identificador con session_regenerate_id(). Con esto tratamos de impedir las fijaciones de sesión, aspecto que trataré de explicar más adelante (si es que al final logro entenderlo).

Otra forma de acceder a pagina2.php sin usar la entrada de la página primera podría ser con el telnet con php que comenté más arriba. Con esta cadena de petición:

GET /temas/php-sesion/ejemplos/proceso-sesion/pagina2.php HTTP/1.1
Host: localhost
cookie: PHPSESSID=falso-id
Connection: Close

Poniendo cualquier identificador de sesión como el señalado en azul, el servidor nos responderá devolviendo la página 2:

HTTP/1.1 200 OK
Date: Thu, 16 Sep 2010 09:41:45 GMT
Server: Apache/2.2.15 (Win32) PHP/5.2.13
X-Powered-By: PHP/5.2.13
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: PHPSESSID=nlikqad9ib1563tgod36ggfa84; path=/
... 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es">
...
<body style="background-color:red;" >
    ...
    <p>Esta página2 tiene el valor <big><big>red</big></big>
    para la propiedad <code>background-color</code> del cuerpo del documento.</p>
    <p style="font-size: 2em;">Ha llegado a esta página sin antes haber pasado
    por la página 1.</p>
    ...

En este caso al no existir una sesión anterior con ese identificador, tampoco habrá un parámetro de color guardado en $_SESSION. Entonces se regenera el identificador tal como podemos ver que se pasa en la cabecera de la respuesta y se pone el color rojo para el fondo de la página. El identificador falso-id que se pasó es guardado en la carpeta Temp con el resto de sesiones, pero al regenerar la sesión con session_regenerate_id(true) usando el argumento true lo que hacemos es eliminar ese archivo de falsa sesión.

En el Tema VI hablaremos de cómo asegurar sesiones, exponiendo con mayor detalle los casos de suplantación de sesiones.


Otro aspecto de las sesiones es el tiempo de vida que poseen. Esto también es una razón por la cual deberíamos inspeccionar en pagina2.php si existen las variables de sesión (el parámetro de color en nuestro ejemplo). Las sesiones que ya no se utilizan son eliminadas del sistema, tanto en memoria como en disco. Esto lo veremos en el próximo tema que habla sobre la destrucción de sesiones.