FlawOpen

flawopen.com/Vulnerabilities/Fixation de session et sécurité des cookies

Fixation de session : renouveler l’ID au login (CWE-384)

High Severity CWE-384 Cryptography & Auth
💡 Explication simple — le ticket de bagage collé

Imaginez qu’un voyageur soit amené à utiliser un numéro de ticket de bagage déjà connu de l’attaquant. Si la compagnie aérienne conserve ce numéro après l’enregistrement, l’attaquant pourrait récupérer la valise. Dans une fixation de session, l’application accepte un identifiant que l’attaquant peut faire utiliser à la victime, puis transforme cet identifiant en session authentifiée sans le renouveler. Les deux conditions sont nécessaires ; un simple lien de phishing ne suffit pas, sauf si l’application accepte ou permet de définir cet identifiant.

Cible : gestion de session côté serveur et identifiant du navigateur authentifié.
Condition préalable : l’attaquant peut faire utiliser à la victime un ID de session connu, par exemple via des ID d’URL acceptés ou une injection de cookie.
Défaut : l’application conserve cet ID connu de l’attaquant lors du passage de la session anonyme à la session authentifiée.
Correction : émettre un nouvel ID imprévisible à l’authentification ; configurer les attributs des cookies selon les menaces.

Concepts clés et termes du sous-système

Fixation de session
Défaut de gestion des sessions où l’application accepte un identifiant connu de l’attaquant et le conserve lorsque la victime s’authentifie.
Rotation de l’ID de session
Émettre un identifiant neuf et imprévisible lorsque l’authentification élève les privilèges de la session. Ne conserver que les données de session que l’application doit garder.
Attributs de cookie
Secure limite l’envoi du cookie à HTTPS ; HttpOnly empêche JavaScript de lire le cookie ; SameSite contrôle une partie des envois intersites. Ces attributs répondent à des risques différents et ne remplacent pas la rotation de l’ID.
Préfixe __Host-
Dans les navigateurs compatibles, exige un cookie Secure défini via HTTPS, avec Path=/ et sans attribut Domain. Il aide à empêcher un sous-domaine frère de définir un cookie du domaine parent portant le même nom.

Déroulement de l’attaque, étape par étape

Step 1

L’attaquant obtient ou choisit un identifiant de session

Une faille ou une fonctionnalité distincte permet à l’attaquant d’obtenir un ID et de faire en sorte que le navigateur de la victime l’envoie. Le moyen dépend de l’application ; un lien de phishing ordinaire ne suffit pas si les ID de session dans l’URL sont refusés.

Step 2

La victime utilise l’identifiant connu de l’attaquant

Le navigateur envoie cet ID à l’application, qui le traite comme une session anonyme valide. L’attaquant doit pouvoir reconnaître ou réutiliser le même identifiant.

Step 3

L’authentification élève les privilèges de la session

La victime se connecte. Si l’application associe l’identité authentifiée à l’ID existant sans le renouveler, cet identifiant connu désigne désormais la session authentifiée.

Step 4

L’attaquant réutilise l’identifiant inchangé

Si l’attaquant peut encore envoyer l’ID et que le serveur ne l’a pas invalidé, il peut accéder à la session de la victime. La rotation lors du changement de privilège rompt cette chaîne.

Mécanisme et cause racine

La fixation de session exige à la fois que l’attaquant influence l’identifiant utilisé par la victime et que l’application ne le remplace pas à l’authentification. Les attributs de cookie renforcent la défense, mais aucun ne remplace la rotation : HttpOnly limite la lecture par script, Secure impose HTTPS, SameSite réduit certains envois intersites et __Host- limite la portée du cookie dans les navigateurs compatibles.

login_view.py (Modèle vulnérable)Vulnérable
# 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 (Exemple Django sécurisé)Renforcé
# 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

Séquence d’attaque et exemple conceptuel

Règles de prévention et de renforcement technique

Sources et sujets de sécurité associés :Répertoire complet de sécurité →
CWE-384 : fixation de session → OWASP : guide de gestion des sessions → OWASP WSTG : tester la fixation de session → Django : sessions et cycle_key() → MDN : cookies HTTP et préfixes →