Retour au cours

cyber / cybersecurite-fondamentale

Sécurité web : cookies sécurisés et CORS

Leçon 151 exercice

Explication

Ce que vous allez apprendre

  • Configurer un cookie de session avec HttpOnly, Secure et SameSite comme réglages par défaut
  • Comprendre le rôle du préfixe __Host- pour durcir encore un cookie sensible
  • Expliquer pourquoi la Same-Origin Policy bloque par défaut les requêtes cross-domaine
  • Configurer CORS avec une liste blanche stricte plutôt qu'une réflexion aveugle de l'en-tête Origin
  • Identifier la combinaison dangereuse allow_credentials=True + wildcard d'origine

Dans quel contexte ?

Un site tiers malveillant embarque un script qui appelle directement l'API interne d'une entreprise depuis le navigateur d'une victime déjà connectée. Si l'API répond avec Access-Control-Allow-Origin réglé sur l'origine du site malveillant et autorise les cookies, le navigateur laisse passer la requête authentifiée sans broncher. Une configuration CORS avec liste blanche stricte aurait bloqué cet appel avant même qu'il n'atteigne le serveur.

Un cookie de session est ce qui permet à un site de se souvenir que vous êtes connecté d'une requête à l'autre. Mal protégé, c'est aussi l'un des vecteurs de vol de session les plus recherchés par un attaquant : le récupérer équivaut souvent à usurper complètement l'identité de la victime, sans même connaître son mot de passe.

Trois attributs qui changent tout

AttributCe qu'il protègeContre quelle attaque
HttpOnlyInterdit la lecture du cookie en JavaScriptVol de session via XSS (leçon 9)
SecureInterdit la transmission du cookie en clairInterception réseau
SameSiteContrôle l'envoi du cookie vers des sites tiersCSRF (leçon dédiée)

Ensemble, ces trois attributs devraient être la configuration par défaut de TOUT cookie de session, sans exception.

CORS : une porte que le navigateur garde fermée par défaut

Par défaut, le navigateur empêche une page chargée depuis un domaine d'appeler l'API d'un autre domaine — c'est la "same-origin policy". CORS est le mécanisme qui permet à un serveur d'ouvrir explicitement cette porte à certains domaines de confiance, via des en-têtes de réponse HTTP.

Le piège le plus dangereux de cette leçon

Piège fréquent

Un serveur qui répond simplement "oui" à n'importe quelle origine demandée (en reflétant aveuglément l'en-tête Origin reçu) tout en autorisant l'envoi des cookies (allow_credentials=True) annule complètement la protection CORS : n'importe quel site tiers peut alors faire des requêtes authentifiées vers votre API en votre nom. La règle est simple : une liste blanche explicite d'origines de confiance, jamais une réflexion automatique de ce que le client prétend être.

Bonne pratique

Préfixez vos cookies sensibles avec __Host- : le navigateur impose alors automatiquement Secure, l'absence d'attribut Domain et Path=/, ce qui rend le cookie beaucoup plus difficile à falsifier depuis un sous-domaine compromis.

Un détail qui traîne souvent

Les listes blanches CORS accumulent parfois des URL de test ou de staging oubliées après un déploiement — une revue périodique de cette configuration évite qu'un domaine de test compromis devienne une porte d'entrée vers la production.

Commandes & code

Sécurité web : cookies sécurisés et CORS

http
# Attributs de cookie de sécurité
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=28800
text
HttpOnly  : inaccessible en JavaScript (document.cookie) -> mitige le vol de session via XSS
Secure    : cookie envoyé UNIQUEMENT sur HTTPS -> mitige l'interception réseau
SameSite  : contrôle l'envoi cross-site -> mitige le CSRF (voir leçon dédiée)
Path      : restreint le cookie à un chemin précis de l'application
Max-Age / Expires : durée de vie explicite, évite les cookies "éternels"
__Host- (préfixe) : impose Secure + Path=/ + pas de Domain -> le cookie le plus difficile à falsifier
python
# Configuration de cookie sécurisé dans FastAPI
from fastapi import Response

@app.post("/login")
async def login(response: Response, credentials: LoginSchema):
    session_token = create_session(...)
    response.set_cookie(
        key="__Host-session",     # préfixe __Host- : navigateur impose Secure, pas de Domain, Path=/
        value=session_token,
        httponly=True,
        secure=True,
        samesite="strict",
        max_age=28800,             # 8 heures
        path="/",
    )
    return {"status": "connecté"}
text
# CORS (Cross-Origin Resource Sharing) : autorise EXPLICITEMENT un domaine tiers à appeler ton API
# Par défaut, le navigateur bloque les requêtes cross-origin (Same-Origin Policy) - CORS est une exception contrôlée
python
# VULNÉRABLE : CORS trop permissif, reflète n'importe quelle origine avec credentials
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],          # accepte TOUT domaine
    allow_credentials=True,        # ET autorise l'envoi de cookies -> combinaison dangereuse
    allow_methods=["*"],
    allow_headers=["*"],
)
# NOTE : les navigateurs interdisent en réalité "*" + credentials=True simultanément,
# mais des implémentations custom qui reflètent Origin dynamiquement recréent ce trou.
python
# VULNÉRABLE (variante fréquente) : réflexion naïve de l'en-tête Origin sans liste blanche
@app.middleware("http")
async def bad_cors(request: Request, call_next):
    response = await call_next(request)
    origin = request.headers.get("origin", "*")
    response.headers["Access-Control-Allow-Origin"] = origin  # accepte AVEUGLÉMENT n'importe quel Origin
    response.headers["Access-Control-Allow-Credentials"] = "true"
    return response
python
# CORRECT : liste blanche stricte d'origines de confiance
from fastapi.middleware.cors import CORSMiddleware

ALLOWED_ORIGINS = [
    "https://technologik.local",
    "https://app.technologik.local",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=ALLOWED_ORIGINS,   # liste explicite, jamais de wildcard avec credentials
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Content-Type", "Authorization", "X-CSRF-Token"],
    max_age=600,                      # durée de cache du préflight OPTIONS
)
http
# Anatomie d'une requête préflight CORS (envoyée automatiquement par le navigateur avant une requête "non simple")
OPTIONS /api/courses HTTP/1.1
Origin: https://app.technologik.local
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization

# Réponse attendue du serveur pour autoriser la requête réelle qui suivra
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.technologik.local
Access-Control-Allow-Methods: DELETE
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Credentials: true
text
# Checklist cookies & CORS
- HttpOnly + Secure + SameSite sur tout cookie de session, sans exception
- Jamais de wildcard "*" en Access-Control-Allow-Origin quand allow_credentials=True
- Liste blanche explicite d'origines, jamais de réflexion aveugle de l'en-tête Origin
- Vérifier régulièrement que la liste blanche CORS ne contient pas d'environnements de test oubliés

Résumé

  • Les attributs HttpOnly, Secure, SameSite sont la base de tout cookie de session.
  • Le préfixe __Host- durcit encore la protection contre la falsification de cookie.
  • CORS doit toujours reposer sur une liste blanche explicite, jamais sur une réflexion aveugle de Origin.
  • allow_credentials=True combiné à un wildcard d'origine est une faille à elle seule.

Exercices pratiques

1 disponible
1

Mission : le middleware CORS qui accepte tout le monde

Objectif : Repérer une configuration CORS dangereuse dans du code réel et la remplacer par une liste blanche stricte.

Contexte

Un audit de sécurité de l'API Technologik trouve ce middleware en production :

python
@app.middleware("http")
async def bad_cors(request, call_next):
    response = await call_next(request)
    origin = request.headers.get("origin", "*")
    response.headers["Access-Control-Allow-Origin"] = origin
    response.headers["Access-Control-Allow-Credentials"] = "true"
    return response

Un site totalement étranger à Technologik, recettes-de-cuisine.example, parvient à faire des appels authentifiés vers l'API depuis le navigateur d'un utilisateur déjà connecté.

Résoudre l’exercice →