FlawOpen

flawopen.com/Vulnerabilities/Fijación de sesión y seguridad de cookies

Fijación de sesión: renovar el ID al entrar (CWE-384)

High Severity CWE-384 Cryptography & Auth
💡 Explicación sencilla — la etiqueta de equipaje pegada

Imagina que engañan a un viajero para que use un número de etiqueta de equipaje que el atacante ya conoce. Si la aerolínea conserva ese número después de facturar, el atacante podría recoger la maleta. En la fijación de sesión, la aplicación acepta un identificador que el atacante puede hacer usar a la víctima y luego convierte ese mismo identificador en una sesión autenticada sin renovarlo. Se necesitan ambas condiciones; un enlace de phishing por sí solo no fija una sesión, salvo que la aplicación acepte o permita establecer ese identificador.

Objetivo: la gestión de sesiones del servidor y el identificador del navegador autenticado.
Condición previa: el atacante puede hacer que la víctima use un ID de sesión conocido, por ejemplo, mediante IDs en URL aceptados o inyección de cookies.
Fallo: la aplicación conserva ese ID conocido por el atacante al pasar de una sesión anónima a una autenticada.
Corrección: emite un ID nuevo e impredecible al autenticar; configura atributos de cookie para amenazas distintas.

Conceptos clave y términos del subsistema

Fijación de sesión
Fallo de gestión de sesiones en el que la aplicación acepta un identificador conocido por el atacante y lo conserva cuando la víctima se autentica.
Rotación del ID de sesión
Emite un identificador nuevo e impredecible cuando la autenticación eleva los privilegios de la sesión. Conserva solo los datos de sesión que la aplicación deba mantener.
Atributos de cookie
Secure limita el envío de la cookie a HTTPS; HttpOnly impide que JavaScript lea la cookie; SameSite controla parte del envío entre sitios. Atienden riesgos distintos y no sustituyen la rotación del ID.
Prefijo __Host-
En navegadores compatibles, exige una cookie Secure creada por HTTPS, con Path=/ y sin atributo Domain. Ayuda a impedir que un subdominio hermano establezca una cookie del dominio padre con el mismo nombre.

Flujo de ataque paso a paso

Step 1

El atacante obtiene o elige un identificador de sesión

Una debilidad o función independiente permite al atacante obtener un ID y hacer que el navegador de la víctima lo envíe. La vía depende de la aplicación; un enlace de phishing corriente no basta si no se aceptan IDs de sesión en URL.

Step 2

La víctima utiliza el identificador conocido

El navegador envía ese ID a la aplicación, que lo trata como una sesión anónima válida. El atacante debe poder reconocer o reutilizar el mismo identificador.

Step 3

La autenticación cambia los privilegios de la sesión

La víctima inicia sesión. Si la aplicación asocia la identidad autenticada al ID existente en vez de renovarlo, ese identificador conocido pasa a representar la sesión autenticada.

Step 4

El atacante reutiliza el identificador sin cambios

Si el atacante aún puede enviar el ID y el servidor no lo ha invalidado, podría acceder a la sesión de la víctima. Renovar el ID al cambiar los privilegios rompe esta cadena.

Mecanismo y causa raíz

La fijación de sesión requiere que el atacante influya en el identificador que utiliza la víctima y que la aplicación no lo sustituya al autenticar. Los atributos de cookie aportan defensa en profundidad, pero ninguno sustituye la rotación: HttpOnly limita la lectura desde scripts, Secure limita el transporte a HTTPS, SameSite reduce algunos envíos entre sitios y __Host- restringe el alcance de la cookie en navegadores compatibles.

login_view.py (Patrón vulnerable)Vulnerable
# VULNERABLE: custom session code keeps the pre-authentication ID.
def login_view(request):
    user = authenticate(request, request.POST["username"],
                        request.POST["password"])
    if user is not None:
        # BUG: privilege changes, but the session ID does not.
        request.session["user_id"] = user.pk
        request.session["authenticated"] = True
        return redirect("/account/")
    return redirect("/login/")
login_view.py (Ejemplo Django seguro)Protegido
# HARDENED: Django login() rotates the session key on authentication.
from django.contrib.auth import authenticate, login

def login_view(request):
    user = authenticate(request, request.POST["username"],
                        request.POST["password"])
    if user is not None:
        # Uses Django's supported authentication/session integration.
        login(request, user)
        return redirect("/account/")
    return redirect("/login/")

# settings.py: use HTTPS, host-only scope, and a deliberate SameSite policy.
SESSION_COOKIE_NAME = "__Host-sessionid"
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_PATH = "/"
SESSION_COOKIE_DOMAIN = None

Secuencia del ataque y ejemplo conceptual

Reglas de prevención y refuerzo para ingeniería

Fuentes y temas de seguridad relacionados:Directorio completo de seguridad →
CWE-384: fijación de sesión → OWASP: guía de gestión de sesiones → OWASP WSTG: prueba de fijación de sesión → Django: sesiones y cycle_key() → MDN: cookies HTTP y prefijos →