El identificador de la relación servidor-cliente en una sesión

En los temas anteriores se hizo una exposición de las sesiones realizando ejemplos con sesiones basadas en cookies. Parecen ser la forma más segura pero también hay otras formas de propagar el identificador de sesión, que recordemos que es un vínculo que relaciona el servidor con un cliente determinado. Este identificador debe alojarse en el navegador del cliente con la finalidad de que en sucesivas peticiones sea remitido al servidor. Así éste podrá seleccionar las variables de sesión individuales para ese único cliente.

En el navegador del cliente podemos alojar el identificador de sesión de al menos tres maneras:

  1. En una cookie que envía el servidor y que el cliente devolverá con cada petición.
  2. El servidor lo aplica en todos los elementos que tengan un vínculo. Así el cliente cuando active unos de esos vínculos, el identificador llegará al servidor como un parámetro URL.
  3. Como campos ocultos de formularios que se envían por POST. Este es similar al anterior, aunque con importantes diferencias en cuanto a seguridad.

En el primer tema, en el apartado sobre el proceso de una sesión PHP, expusimos las siguientes configuraciones de PHP relacionadas con la forma en que se propagan las sesiones.

  • session.use_cookies: Con valor activado PHP usará cookies para la propagación, pero si éstas estuvieran desactivadas en el navegador entonces usará URL a menos que la siguiente opción esté activada. Si está desactivado no usará cookies en ningún caso.
  • session.use_only_cookies: Si ésta y la anterior están activadas, sólo se usarán cookies. Si ambas están desactivadas sólo se usará URL.
  • session.use_trans_sid: Para el caso de URL, si ésta opción está activada, el servidor construirá parámetros URL en todos los elementos que contengan vínculos en la página. El parámetro será el SID, una cadena que concatena el nombre de la sesión y el identificador interponiendo el signo igual nombre_sesión=identificador_sesión, siguiendo los estándares de paso de parámetros por la URL. Por eso la denominación usar SID transparente (use_trans_sid), pues se adjunta el SID en los vínculos de forma transparente para el programador. Los elementos afectados por el SID pueden configurarse en la opción url_rewriter.tags, cuyo valor por defecto es a=href,area=href,frame=src,input=src,form=fakeentry,fieldset=. Se trata de una cadena de parejas elemento=atributo, de tal forma que es en ese atributo donde se incorporará el identificador. Si por ejemplo en nuestro HTML tenemos un elemento como <a href="otra-pagina.html">... entonces PHP agregará <a href="otra-pagina.html?SID" >..., . Para el elemento <form> si se incluye la palabra clave fakeentry entonces PHP incluirá una nuevo campo de texto oculto con el identificador. Si no queremos esto hemos de cambiarlo por form=action para que lo agregue a la URL. Si desactivamos session.use_trans_sid tendremos que agregar manualmente los SID en el código a cada vínculo.
PHP dispone de la constante SID que se actualiza después de hacer un session_start() con el mismo resultado que la cadena session_name()."=".session_id(). Pero en este documento cuando hablemos de SID en general nos estaremos refiriendo a la pareja nombre e identificador de sesión, que son los dos valores a propagar para conseguir la identificación, mientras que escribiremos constante SID en los casos en que deseemos referirnos a esa propia constante de PHP.

En base a como se combinan estas opciones, que son configurables en tiempo de ejecución con ini_set(), podemos establecer la forma de propagación. En esta tabla resumimos todos los ejemplos que desarrollaremos en esta página, donde 1 indica activado y 0 desactivado:

Propagaciónsession.use_cookiessession.use_only_cookiessession.use_trans_sidurl_rewriter.tagsNotas
Sólo cookies110 (indiferente)indiferenteOpción por defecto en php.ini 5.2.13
Sólo URL con trans_sid y form=fakeentry001...form=fakeentry...
Sólo URL con trans_sid y form=action001...form=action...
Sólo URL sin trans_sid000indiferente
Cookies o URL con trans_sid101...form=fakeentry...
Cookies o URL sin trans_sid100indiferenteUsando constante SID
Sólo POST000indiferenteIgual que sólo URL sin trans_sid, pero sólo usando POST

La razón de tantas posibilidades se debe a que el servidor, en este caso PHP, trata de cumplir el estándar RFC2965 sobre el mecanismo de estado de sesiones basadas en cookies, pues en su punto 2 expone que 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 si el navegador no acepta cookies, el servidor podría disponer de otras posibilidades para continuar usando sesiones. Aunque también se advierte que la forma más segura es usando sólo cookies, como veremos en un tema posterior sobre seguridad.

Esquema básico para todos los ejemplos

Se trata de elaborar un conjunto de 4 páginas para cada uno de los ejemplos anteriores:

  • Una primera página llamada propaga0.php que será una página de entrada sin sesión. Tiene dos cometidos principales:
    1. Sirve de entrada y salida de la sesión. En las siguientes páginas con sesión si necesitáramos cerrar la sesión, nos remitiría aquí.
    2. Para la propagación con cookies, nos sirve para enviar una cookie para testear si el navegador las tiene activas. Esta cookie de prueba (que denominamos test-cookie) no es una cookie de sesión. En la siguiente página donde iniciamos sesión haríamos una consulta para ver si nos llegó esta cookie.
  • Una segunda página propaga1.php que sería la página de inicio de sesión. Aquí hacemos el primer session_start() y damos valor por primera vez a una variable de sesión que llamaremos $_SESSION["iniciada"]. Así comprobaremos en las siguientes páginas que esta variable se está propagando con la sesión.
  • Una tercera página propaga2.php que recogerá la variable de sesión. También se insertarán un par de formularios para ver cómo se reciben sus campos en la siguiente página.
  • Una cuarta página propaga3.php que también recogerá la variable de sesión y los campos recibidos, en su caso, de la página anterior.

El código PHP de todos los ejemplos es similar, por lo que sólo se expone el código del ejemplo número 7 propagar SID con cookies o URL y con trans_sid, pues el más completo y el resto tiene muy pocas variaciones, en todo caso menos líneas de código. Las diferencias se exponen en cada apartado. Si lo desea puede descargar este archivo comprimido propaga-sesiones.zip (70 KB) con todos los códigos en formato de archivos de texto plano (con extensión cambiada a "txt").

Propagar SID: Sólo cookies

Este primer ejemplo puede verse desde el enlace solo-cookies/propaga0.php. La ejecución de las páginas presenta los estados de las variables y configuraciones relacionadas con la sesión que ayudarán a comprender mejor el proceso.

La página de entrada sin sesión propaga0.php sólo tiene un par de líneas de código PHP (en marrón). El HTML (en azul) que no tiene mayor interés lo obviamos. El JavaScript expuesto aquí, que se incluye en todas las páginas, busca las cookies en el navegador, lo que nos servirá para verificar que se están gestionando como se espera. Este script se expone con detalle en el tema I, en el apartado sobre Javascript también puede manejar cookies.

<?php
define("SESION", "sesionCookie");
setcookie("test-cookie", SESION);
?>
...HTML...
    <code id="mi-cookie" style="border: red solid 1px;"> </code>
    <script>
        var arrayCookies = document.cookie.split(";");
        var contenido = "";
        for (var i=0; i<arrayCookies.length; i++){
            var unaCookie = arrayCookies[i].split("=");
            var nombre = quitaEspacios(unaCookie[0]);
            if ((nombre == "test-cookie")||
                (nombre == "<?php echo SESION; ?>")) {
               contenido += escapaHtml(arrayCookies[i]) + "; ";
            }
        }
        if (contenido == "") contenido = "No hay cookies en el navegador.";
        setInnerText(document.getElementById("mi-cookie"), contenido);
    </script>
...sigue HTML...

La constante SESION es simplemente para agilizar el uso del nombre de la sesión en el cuerpo del documento y, sobre todo, es más necesaria en el resto de páginas. La siguiente línea PHP si es importante en este caso. Dado que se trata de sesiones que sólo usarán cookies para propagarse, es necesario detectar si el navegador las tiene activadas. Como la sesión la iniciaremos en la siguiente página propaga1.php, en ese momento se habrá emitido una cookie de sesión al navegador, pero el servidor no sabría si fue aceptada. Así aprovechamos la página de entrada sin sesión para emitir una cookie de prueba (que llamamos test-cookie). Luego en la página de inicio de sesión comprobaremos que el navegador la aceptó si la recibimos.

La página de inicio de sesión es propaga1.php. El script PHP es ahora más extenso. En esta y las siguientes páginas ponemos lo mismo al inicio del código:

ini_set("session.use_cookies", 1);
ini_set("session.use_only_cookies", 1);
ini_set("session.use_trans_sid", 0);
define("SESION", "sesionCookie");
session_name(SESION);
session_start();
Las modificaciones en las opciones del php.ini sólo serán válidas durante la ejecución del script, por lo que habrá que acompañarlas en todas las páginas. A menos que podamos acceder al archivo php.ini y realizarlas ahí. Si nuestro sitio está en un alojamiento compartido, quizás esto no sea posible, pero incluso puede resultar una medida positiva la ejecución de los ini_set() en cada script, por si alguién le da por modificar el php.ini del alojamiento compartido.

Se trata de establecer las opciones para usar sólo cookies, como vimos en la tabla del primer apartado. Para cada ejemplo usamos un nombre distinto de sesión, siendo en este caso sesionCookie. Luego iniciamos sesión. A continuación vemos si hemos recibido la cookie de prueba:

$hay_cookie = (((isset($_COOKIE["test-cookie"]))&&
    (($_COOKIE["test-cookie"]) == SESION))||
    (isset($_COOKIE[SESION])));
if ($hay_cookie) setcookie("test-cookie", "", time() - 42000);

Si es la primera vez que entramos en esta página con sesión no iniciada, no existirá $_COOKIE[SESION], por lo que tendremos que comprobar $_COOKIE["test-cookie"] que viene desde la página de entrada. Si $hay_cookie es true entonces el navegador tiene las cookies activadas y podemos eliminar la cookie de prueba. El código sigue con una acción para finalizar la sesión y llevarnos a la página de entrada sin sesión (propaga0.php):

if (isset($_POST["fin-sesion"])){
    $_SESSION = array();
    if (ini_get("session.use_cookies")) {
        $params = session_get_cookie_params();
        setcookie(session_name(), '', time() - 42000,
            $params["path"], $params["domain"],
            $params["secure"], $params["httponly"]
        );
    }
    session_destroy();
    $host  = $_SERVER["HTTP_HOST"];
    $uri   = rtrim(dirname($_SERVER["PHP_SELF"]), "/\\");
    $pagina = "propaga0.php";
    header("Location: http://$host$uri/$pagina");
    exit;
}

Este código para finalizar sesión ya se expuso en el tema II, en el apartado sobre destrucción de sesiones, por lo que no comentamos más nada aquí. Finalmente el script PHP acaba registrando la variable de sesión por primera vez:

if (!isset($_SESSION["iniciada"])) {
    $_SESSION["iniciada"]  = "SESION INICIADA CON SOLO COOKIES";
}

Esta variable será la que nos indique en los siguientes accesos si se está propagando la sesión. Las siguientes páginas, propaga2.php y propaga3.php no tienen estas últimas líneas, mientras que el resto es igual a excepción de la verificación de cookies activadas. En este caso sólo basta con verificar que se ha recibido la cookie de sesión:

$hay_cookie = isset($_COOKIE[SESION]);

Con el ejemplo en ejecución pueden verse los detalles acerca de las variables y configuraciones. Puede probarse desactivando las cookies del navegador y ver que la sesión no se propaga por las páginas, creando una nueva sesión en cada acceso.

Propagar SID: Sólo URL con trans_sid y form=fakeentry

La ejecución se inicia en este enlace solo-url-transsid-fake/propaga0.php. El código de la página de entrada sólo contiene la definición de la constante SESION. No es necesario enviar una cookie de prueba pues no se utilizarán para las sesiones. En la página de inicio de sesión, propaga1.php hacemos lo mismo que antes, pero adaptando la configuración a sólo URL con SID transparente y fakeentry para los formularios (ver tabla del primer apartado):

ini_set("session.use_cookies", 0);
ini_set("session.use_only_cookies", 0);
ini_set("session.use_trans_sid", 1);
ini_set("url_rewriter.tags", "a=href,form=fakeentry");
define("SESION", "sesionUrlTranssidFake");
session_name(SESION);
session_start();
if (isset($_POST["fin-sesion"])){
    ...finalizamos sesión....
}  
if (!isset($_SESSION["iniciada"])) {
    $_SESSION["iniciada"]  = "SESIÓN INICIADA CON SÓLO URL, 
    TRANSSID Y FORM=FAKEENTRY";
}

La única diferencia es que no detectamos ninguna cookie, pues ahora no hace falta. Observamos el nuevo nombre de sesión que usaremos en este ejemplo sesionUrlTranssidFake. Además ahora especificamos la reescritura de tags a=href y form=fakeentry. Esta página de entrada de sesión tiene dos vínculos. Por un lado un formulario con un botón para finalizar sesión, cuyo vínculo en el atributo action la llama a si misma. Luego hay otro vínculo que apunta a la siguiente página propaga2.php. Si ejecutamos el ejemplo y vemos el código fuente generado en nuestro navegador, observaremos esto:

...
<form action="propaga1.php" method="post">
<input type="hidden"
name="sesionUrlTranssidFake" 
value="jbspfclsdtfhm57fe104n3r8a6" />
<input name="fin-sesion" type="submit" value="finalizar sesión" />
</form>
....
<li>Vínculo a otra página 
<a 
href="propaga2.php?sesionUrlTranssidFake=jbspfclsdtfhm57fe104n3r8a6">
propaga2.php</a></li>
...

Si observamos el vínculo <a>, vemos que PHP ha agregado el parámetro SID, es decir, nombre_sesión = identificador_sesión. Por otro lado PHP ha generado un elemento <input type="hidden" > en el formulario para albergar el SID de la sesión.

Esto lo hace PHP de forma transparente para el programador, teniendo en cuenta la configuración establecida en url_rewriter.tags, cuyo valor por defecto es a=href,area=href,frame=src,input=src,form=fakeentry,fieldset=, pero que hemos reescrito sólo para a=href,form=fakeentry pues el resto no los vamos a utilizar en este ejemplo. Así para los elementos <a> usará el agregado de parámetro en el atributo href, mientras que para los formularios la palabra clave fakeentry le dice que debe crear un campo oculto para ubicar el SID.

Si nos situamos en el vínculo que lleva a la siguiente página propaga2.php comprobaremos que la barra de estado del navegador contiene la URL con el parámetro de sesión. Al pasar a esa página observamos, en el apartado de variables de sesión, que ésta se recibió con $_GET["sesionUrlTranssidFake"]: jbspfclsdtfhm57fe104n3r8a6. Es decir, se recibe el parámetro con el SID por GET en la URL. Pero también lo podemos recibir por POST.

En esa misma página propaga2.php tenemos un formulario que remite por POST su campo a la página propaga3.php. Si lo ejecutamos veremos en el apartado de variables de sesión lo siguiente:

$_GET["sesionUrlTranssidFake"]: NO EXISTE
$_POST["sesionUrlTranssidFake"]: jbspfclsdtfhm57fe104n3r8a6
$_GET["campo-get"] del formulario GET: NO EXISTE
$_POST["campo-post"] del formulario POST: Valor del input post
    

En definitiva, PHP estará vigilando los GET y POST para el recibido de un parámetro con el nombre de la sesión. Entonces lo compara con las sesiones iniciadas y si el identificador coincide podrá completar el proceso de identificación de sesión tal como hacía en el caso de cookies.

Si tenemos un vínculo a un sitio externo, PHP no agrega el SID. Por ejemplo, en la última página propaga3.php tenemos estos tres vínculos generados en nuestro navegador:

...
<li>Volver a la primera página 
<a href="propaga1.php?sesionUrlTranssidFake=jbspfclsdtfhm57fe104n3r8a6">
propaga1.php</a></li>
<li>Volver a la segunda página 
<a href="propaga2.php?sesionUrlTranssidFake=jbspfclsdtfhm57fe104n3r8a6">
propaga2.php</a></li>            
<li>Enlace a un sitio externo
<a href="http://docs.php.net/manual/es/">http://docs.php.net/manual/es/</a>
</li>        
...

PHP agregó el SID a los dos primeros mientras que no lo hizo con el último, supongo porque detectó http:// al inicio. De todas formas dejar en manos de PHP que realice estas acciones puede comprometer la seguridad al exponer un SID con una URL a un sitio externo, en el caso de que por alguna razón algo no vaya bien. Luego veremos que podemos mantener un mayor control sin utilizar use_trans_sid, aunque suponga más esfuerzo pues habrá que escribir todos los agregados de SID.

Propagar SID: Sólo URL con trans_sid y form=action

La ejecución se inicia en este enlace solo-url-transsid-action/propaga0.php. Este ejemplo es igual que el anterior, propagando la sesión sólo con URL, pero usando el atributo action en la opción url_rewriter.tags en lugar de fakeentry. Las configuraciones iniciales son:

ini_set("session.use_cookies", 0);
ini_set("session.use_only_cookies", 0);
ini_set("session.use_trans_sid", 1);
ini_set("url_rewriter.tags", "a=href,form=action");
define("SESION", "sesionUrlTranssidAction");
session_name(SESION);
session_start();

Al solicitar la página propaga2.php podemos ver el código fuente generado en nuestro navegador:

...
<form 
action="propaga2.php?sesionUrlTranssidAction=1375aptlieg4arkrsm0rfjpj26" 
method="post">
<input type="hidden" name="sesionUrlTranssidAction" 
value="1375aptlieg4arkrsm0rfjpj26" /> 
<input name="fin-sesion" type="submit" value="finalizar sesión" />
</form>
.....
<a href="propaga1.php?sesionUrlTranssidAction=1375aptlieg4arkrsm0rfjpj26"> 
propaga1.php</a>
...
<form action="propaga3.php?sesionUrlTranssidAction=1375aptlieg4arkrsm0rfjpj26" 
method="get">
<input type="hidden" name="sesionUrlTranssidAction" 
value="1375aptlieg4arkrsm0rfjpj26" />
<label>campo-get:
<input type="text" name="campo-get" value="Valor del input get" />
</label>
<input type="submit" value="enviar" />
</form>
...
<form 
action="propaga3.php?sesionUrlTranssidAction=1375aptlieg4arkrsm0rfjpj26" 
method="post">
<input type="hidden" name="sesionUrlTranssidAction"
value="1375aptlieg4arkrsm0rfjpj26" />
...

En los elementos formulario vemos que PHP agregó el SID en el atributo action de la misma forma que para los elementos vínculo, pero también dotó a los formularios del campo de texto oculto. Si desde esta página propaga2.php envíamos el formulario POST, podemos observar en propaga3.php este resultado:

$_GET["sesionUrlTranssidAction"]: 1375aptlieg4arkrsm0rfjpj26 
$_POST["sesionUrlTranssidAction"]: 1375aptlieg4arkrsm0rfjpj26 
$_GET["campo-get"] del formulario GET: NO EXISTE 
$_POST["campo-post"] del formulario POST: Valor del input post 
    

Se recibe el SID por ambas vías, el GET del parámetro URL agregado al action del formulario y el POST del campo oculto.

Propagar SID: Sólo URL sin trans_sid

La ejecución se inicia en este enlace solo-url-no-transsid/propaga0.php. Las configuraciones iniciales son:

ini_set("session.use_cookies", 0);
ini_set("session.use_only_cookies", 0);
ini_set("session.use_trans_sid", 0);
define("SESION", "sesionUrlNoTranssid");
session_name(SESION);
session_start();

Ahora el programador debe incluir los SID manualmente en cada vínculo. Por ejemplo, la página propaga2.php tiene los elementos con vínculos con el SID de esta forma antes de que el script se procese, donde en amarillo aparece resaltado lo que hemos tenido que agregar en el código:

...
<a href="propaga1.php?<?php echo SESION."=".session_id(); ?>">
propaga1.php</a></li>    
...
<form action="propaga3.php" method="get">
<input type="hidden" name="<?php echo SESION; ?>"
value="<?php echo session_id(); ?>" />
<label>campo-get:
<input type="text" name="campo-get" value="Valor del input get" />
</label>
<input type="submit" value="enviar" />
</form>
...
<form action="propaga3.php" method="post">
<input type="hidden" name="<?php echo SESION; ?>"
value="<?php echo session_id(); ?>" />
<label>campo-post:
<input type="text" name="campo-post" value="Valor del input post" />
</label>
<input type="submit" value="enviar" />
</form>
...
Como hemos mencionado en comentarios anteriores, PHP dispone de la constante SID que se actualiza después de hacer un session_start() con el mismo resultado que la cadena session_name()."=".session_id(). La ventaja es que si estamos propagando por cookies, esta constante será una cadena vacía, por lo que podemos usar este método con un ejemplo que soporte propagación por cookies o URL sin trans_sid, como veremos en un apartado más abajo. En el ejemplo actual podríamos usar esa constante para no tener que estar escribiendo nombre e identificador, pero esto sólo nos sirve para los vínculos URL, pues en los campos ocultos de formulario tenemos que ponerlo por separado.

En el código resultante en el navegador veremos esto para una sesión en ejecución:

...
<a href="propaga1.php?sesionUrlNoTranssid=4uuunt9u3jtebkr9afscpkrvp1">
propaga1.php</a>
...
<form action="propaga3.php" method="get">
<input type="hidden" name="sesionUrlNoTranssid"
value="4uuunt9u3jtebkr9afscpkrvp1" />
...
</form>
...
<form action="propaga3.php" method="post">
<input type="hidden" name="sesionUrlNoTranssid"
value="4uuunt9u3jtebkr9afscpkrvp1" />
...
</form>
...

Cuando veamos los temas de seguridad, comprenderemos que no es conveniente propagar el SID por URL, es decir, vía GET. Así en este ejemplo se propaga por POST sólo para el formulario con method="post", pues para los que tienen method="get" y para los vínculos <a> lo hará por GET. Podemos obligarnos sólo a incluir formularios POST, pero los vínculos <a> sólo podrán propagar la sesión como un parámetro URL (aunque al final del tema veremos una forma de evitarlo).

Propagar SID: Cookies o URL con trans_sid

La ejecución se inicia en este enlace cookies-url/propaga0.php. En este ejemplo PHP usará cookies si están activadas y en otro caso agregará el SID a los vínculos. El inicio del PHP de las páginas de sesión es:

ini_set("session.use_cookies", 1);
ini_set("session.use_only_cookies", 0);
ini_set("session.use_trans_sid", 1);
ini_set("url_rewriter.tags", "a=href,form=fakeentry");
define("SESION", "sesionCookieUrl");
session_name(SESION);
session_start();    
...

El ejemplo es exactamente igual que el del primer apartado con sólo cookies, pero cambiando este inicio de configuraciones. El use_trans_sid se encarga de agregar los SID a los vínculos de forma transparente. Así parece que este ejemplo consigue evitar la desactivación de las cookies por parte del usuario, asegurando la propagación de la sesión en todos los casos.

Cuando se propaga por cookies sucederá que en la página de inicio de sesión propaga1.php, sólo la primera vez que se inicia una sesión, los vínculos de esa página propagarán por URL, aparte de que también envíe la cookie. Esto sucede porque PHP no ha recibido aún alguna cookie de sesión y decide enviar el SID también por URL. Esto lo podemos ver en el código fuente generado en nuestro navegador en esa primera entrada a la página:

...
Vínculo a otra página 
<a href="propaga2.php?sesionCookieUrl=fttcg55uddc4oi0qvshqc27bs6">
propaga2.php</a>
...

Mientras que si volvemos a esa misma página propaga1.php después de haber estado en otra, o bien simplemente la recargamos, entonces ya no aparecerá el parámetro del vínculo, pues PHP ya habrá recibido la cookie de sesión del navegador.

Propagar SID: Cookies o URL sin trans_sid y usando constante SID

La ejecución se inicia en este enlace cookies-url-sid/propaga0.php. El ejemplo es igual que el del apartado anterior cookies o url, pero ahora sin utilizar trans_sid. Las configuraciones iniciales son:

ini_set("session.use_cookies", 1);
ini_set("session.use_only_cookies", 0);
ini_set("session.use_trans_sid", 0);
define("SESION", "sesionCookieUrlSid");
session_name(SESION);
session_start();

La constante SID es construida por PHP concatenando session_name()."=".session_id(). La única ventaja es que usando un modo de configuración con propagación por cookies-url como el de este ejemplo, si PHP detecta que recibe sesión por cookie entonces esa constante será una cadena vacía. En otro caso se construye como hemos dicho. Así podemos incorporar la constante en nuestro código y no preocuparnos cuando la propagación sea por cookies pues no se agregará el parámetro de sesión en la URL. Por ejemplo, la página propaga2.php, la parte de vínculos y formularios sería escrita como sigue:

...    
<a href="propaga1.php?<?php echo SID; ?>">propaga1.php</a></li>    
...
<a href="propaga3.php?<?php echo SID; ?>">propaga3.php</a></li>
...
<!--  En el caso de un form GET no podemos agregar el SID en el
action, pues en el envío el navegador lo sustituye por los campos
que va a enviar -->
<form action="propaga3.php" method="get">
    <input type="hidden" name="<?php echo SESION; ?>"
    value="<?php echo session_id(); ?>" />
    <label>campo-get:
    <input type="text" name="campo-get" value="Valor del input get" />
    </label>
    <input type="submit" value="enviar" />
</form> 
...
<form action="propaga3.php?<?php echo SID; ?>" method="post">
    <label>campo-post:
    <input type="text" name="campo-post" value="Valor del input post" />
    </label>
    <input type="submit" value="enviar" />
</form>   
...

Cuando las cookies esten activadas, entonces los vínculos no contienen los parámetros pues la constante SID será una cadena vacía (""). Cuando estén desactivadas esa constante será el SID que se agregará como parámetro. Puede probar el ejemplo activando y desactivando cookies y observando el código fuente generado en su navegador.

El comentario HTML hace referencia a que si agregamos el parámetro en el formulario GET, en el envío del mismo parece sustituirlo por lo parámetros que se remiten. Por eso ahí se incluye como campo oculto.

Debe observarse que este ejemplo tiene el mismo problema que comentamos en los párrafos finales del apartado anterior, pues usando cookies, en el primer inicio de sesión agrega el SID en la URL dado que aún no ha recibido cookie de sesión.

Propagar SID: sólo POST

También es posible no enviar el SID por URL ni mediante cookies, sino únicamente usando campos ocultos en formularios POST. Toda petición que se haga al servidor, incluso la de vínculos <a>, se realizará con formularios POST. De esta forma no dependemos de que el usuario tenga activadas las cookies y, por otro lado, no exponemos el SID en las URL. Sin embargo sería necesario que el navegador tenga activado JavaScript. La ejecución se inicia en este enlace solo-post/propaga0.php. El ejemplo es igual que el del apartado sólo URL sin trans_sid, con las mismas configuraciones iniciales:

ini_set("session.use_cookies", 0);
ini_set("session.use_only_cookies", 0);
ini_set("session.use_trans_sid", 0);
define("SESION", "sesionPost");
session_name(SESION);
session_start();

Entonces la propagación de SID hemos de realizarla manualmente en el código. Pero nos obligaremos a que toda clase de vínculos se realice mediante un formulario POST. Así sólo tendremos formularios con method="post" y en ningún caso usaremos el method="get". En cada formulario agregaremos el campo oculto con el SID.

Para todos los vínculos <a> de una página usaremos un único formulario que podemos disponer antes de la aparición del primero de los vínculos. Lo completaremos con el campo oculto que porta el SID:

<form action="" method="post" id="form-vinculos">
    <input type="hidden" name="<?php echo SESION; ?>"
    value="<?php echo session_id(); ?>" />    
</form>

Note que el atributo action es una cadena vacía. Al no contener elementos visibles, este formulario no aparecerá en pantalla, aunque en el ejemplo en ejecución le agregamos un borde rojo para poder apreciarlo. Luego todos los vínculos de la páginas los escribiremos de esta forma:

<a href="javascript:irA('propaga2.php');">propaga2.php</a>

El destino del vínculo será el argumento de la función irA(), que se ejecutará mediante script de JavaScript que ubicaremos en el encabezado de cada página:

<script>
    function irA(destino){
        var formulario = document.getElementById("form-vinculos");
        formulario.action = destino;
        formulario.submit();
    }
</script>

Este script identifica el formulario de vínculos y le dota del destino al atributo action. Luego el método submit() de JavaScript permite remitir el formulario como si hubiésemos pulsado un botón con tipo submit.

Las ventajas de este ejemplo es que no dependemos de las cookies y no exponemos el SID en la URL. Por contra se basa en que el usuario tenga activado JavaScript. Otro problema es que cuando el usuario se sitúa sobre un vínculo no verá el destino en la barra de estado del navegador, sino el literal javascript:irA('un_destino');. Esto puede condicionar al usuario pues no se le está ofreciendo una cadena URL clásica, aunque en ese texto aún puede ver el destino al que se dirige.