CSRF, fijación de sesión y caducidad: lo que la cookie segura no cubre
Tener HttpOnly, Secure y SameSite bien puestos no cierra el ciclo de vida de una sesión. Hay ataques que no roban la cookie: la aprovechan.

Marcar una cookie de sesión con HttpOnly, Secure y SameSite tapa el transporte y el acceso desde JavaScript, pero no cierra el ciclo de vida completo. Una sesión se crea, se autentica, se usa, se refresca, expira y se destruye, y en cualquiera de esos puntos se puede romper. Hay tres fallos que suelen quedarse fuera de la checklist habitual: CSRF, fijación de sesión y caducidad.
El navegador como cómplice
El CSRF se apoya en algo cómodo para el usuario: el navegador manda las cookies solo. El atacante no necesita conocer el identificador de sesión de nadie. Basta con que la víctima, ya autenticada en su banco, cargue una página ajena que incluya un formulario oculto apuntando al endpoint de transferencias y lo dispare con un script. El navegador adjunta la cookie al ir hacia el dominio del banco y el servidor recibe lo que parece una petición autenticada legítima. El atacante no roba la sesión, la usa.
La defensa clásica es el token CSRF: un valor aleatorio asociado a la sesión que viaja en el formulario y que el servidor exige además de la sesión válida. Si falta o no cuadra, la petición se rechaza. SameSite reduce la superficie, pero merece la pena entender el CSRF como problema propio en lugar de dar por hecho que un atributo lo resuelve todo.
Fijación: el ataque va al revés
En la fijación de sesión el atacante no adivina ni roba nada. Hace que la víctima empiece a usar un identificador que él ya conoce. Si la aplicación mantiene el mismo session_id después del login, ese identificador sigue siendo válido y ahora corresponde a una sesión autenticada. No se robó la contraseña ni se adivinó el valor: el error fue no rotar el identificador cuando cambió el estado de autenticación.
La corrección es directa: regenerar el ID al autenticar, como hace session.regenerate en Express, y rotarlo también cuando cambian los privilegios, por ejemplo al conceder un rol de administrador. Una sesión no debería arrastrar transiciones sensibles con el mismo identificador.
Dos relojes, no uno
Una sesión que no caduca nunca es una ventana enorme si alguien copió el identificador. Se combinan dos mecanismos. El timeout por inactividad cierra la sesión tras un periodo sin peticiones y se refresca con actividad legítima; treinta minutos es un ejemplo típico. El timeout absoluto pone un techo a la vida total de la sesión aunque el usuario no pare, por ejemplo ocho horas. Los valores dependen de la aplicación: no es lo mismo un foro que una banca, y lo razonable es aplicar los dos a la vez.
Lo que queda por ver es cuánto de esto viene activado de fábrica. Muchos frameworks ofrecen la rotación de sesión y los timeouts como opciones que hay que encender, y el token CSRF depende de que cada formulario lo incluya. Mientras eso siga siendo responsabilidad del desarrollador, seguirá habiendo sesiones que sobreviven más de lo que deberían.
