flawopen.com/Vulnerabilities/Fixation de session et sécurité des cookies
Fixation de session : renouveler l’ID au login (CWE-384)
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.
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
Securelimite l’envoi du cookie à HTTPS ;HttpOnlyempêche JavaScript de lire le cookie ;SameSitecontrô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 attributDomain. 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
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.
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.
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.
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.
# 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/")
# 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
- 1Condition préalable : Une faille ou fonctionnalité distincte permet à l’attaquant d’influencer l’ID envoyé par le navigateur de la victime.
- 2ID connu : L’attaquant peut reconnaître ou réutiliser le même ID de session anonyme.
- 3Authentification : La victime se connecte tandis que le navigateur continue d’envoyer l’ID connu.
- 4Absence de rotation : L’application élève la session existante au lieu de remplacer son identifiant.
- 5Impact conditionnel : L’attaquant n’accède à la session authentifiée que tant que l’ID connu reste valide et utilisable.
Règles de prévention et de renforcement technique
- ✓Faire tourner l’ID lors d’un changement de privilège : Émettre un nouvel ID imprévisible à la connexion et lors des autres élévations de privilège ; invalider l’ancien selon le comportement documenté du framework.
- ✓Utiliser les API d’authentification du framework : Préférer l’intégration de connexion et de session prise en charge (pour Django,
django.contrib.auth.login()) à l’ajout manuel de l’identité dans la session. - ✓Refuser les ID contrôlés par l’attaquant : Ne pas accepter les ID de session dans les URL. Empêcher l’injection de cookies par des sous-domaines frères et tester toute logique personnalisée de cookie ou de session.
- ✓Configurer chaque attribut selon sa fonction : Utiliser
SecureetHttpOnly; envisager le préfixe__Host-avecPath=/et sansDomain. ChoisirSameSite=LaxouStrictselon les flux de connexion intersites légitimes ; SameSite ne remplace pas les jetons CSRF lorsqu’ils sont nécessaires. - ✓Vérifier la transition : Dans un test automatisé, relever l’ID anonyme, authentifier, vérifier que l’ID a changé et confirmer que l’ancien ID ne donne pas accès à la session authentifiée.