Retour au cours

cyber / cybersecurite-fondamentale

Sécurité web : headers de sécurité et CSP

Leçon 141 exercice

Explication

Ce que vous allez apprendre

  • Comprendre que les headers de sécurité sont des instructions données au navigateur, pas au serveur
  • Configurer HSTS, X-Frame-Options et X-Content-Type-Options pour couvrir les cas les plus courants
  • Écrire une Content-Security-Policy restrictive pour limiter l'impact d'un XSS résiduel
  • Expliquer pourquoi unsafe-inline affaiblit fortement une CSP et comment utiliser un nonce à la place
  • Déployer une CSP progressivement grâce au mode Report-Only avant de l'activer en mode strict

Dans quel contexte ?

Un site e-commerce est victime d'un XSS stored malgré l'échappement en place, à cause d'une bibliothèque tierce mal maintenue. Sans Content-Security-Policy, le script injecté s'exécute librement et exfiltre les données de paiement. Avec une CSP stricte limitant les scripts au seul domaine du site, le navigateur refuse d'exécuter le script injecté, même si l'injection elle-même n'a pas pu être évitée en amont. C'est exactement ce rôle de filet de sécurité que joue la CSP.

Des instructions données au navigateur, pas au serveur

Les headers de sécurité HTTP sont des instructions que le serveur envoie au navigateur pour lui dire comment se comporter face à CE site précis. Ce ne sont pas des protections magiques mais des règles que le navigateur s'engage à respecter — un peu comme des consignes de sécurité affichées à l'entrée d'un bâtiment que les visiteurs (ici, le navigateur) suivent volontairement.

Trois headers simples à fort impact

HeaderCe qu'il empêcheEffet concret
Strict-Transport-Security (HSTS)Le downgrade HTTPS -> HTTPForce le navigateur à toujours utiliser HTTPS pour ce domaine
X-Frame-OptionsLe clickjackingEmpêche le site d'être chargé dans une iframe invisible sur un autre site
X-Content-Type-Options: nosniffLe MIME sniffingEmpêche le navigateur de "deviner" un type de fichier différent de celui annoncé

La CSP, la défense la plus puissante de cette leçon

La Content-Security-Policy va plus loin : elle dit explicitement au navigateur "n'exécute JAMAIS de script qui ne vient pas de ces sources précises". C'est un filet de sécurité redoutable contre le XSS résiduel vu en leçon 9 : même si un attaquant parvient à injecter un <script> dans la page, le navigateur refusera de l'exécuter s'il ne respecte pas la politique définie.

Piège fréquent

Autoriser les scripts inline (unsafe-inline) dans la CSP annule une bonne partie de sa protection contre le XSS, puisque c'est justement le type de code qu'un attaquant injecterait. La bonne pratique est d'utiliser un "nonce" unique généré à chaque requête, que seul le serveur légitime connaît et peut associer aux scripts autorisés.

Une démarche prudente pour déployer une CSP

Bonne pratique

Le mode Content-Security-Policy-Report-Only permet d'observer ce qui SERAIT bloqué sans réellement bloquer, pour ajuster la politique avant de l'activer en mode strict — évite de casser le site en production par surprise.

Commandes & code

Sécurité web : headers de sécurité et CSP

Les headers HTTP de sécurité indiquent au navigateur comment se comporter de manière défensive vis-à-vis d'un site donné.

http
# Panorama des headers de sécurité essentiels
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Content-Security-Policy: default-src 'self'
text
# Rôle de chaque header
HSTS                  : force HTTPS pour toutes les requêtes futures, empêche le SSL stripping
X-Content-Type-Options: empêche le navigateur de "deviner" un type MIME différent (anti-XSS via upload)
X-Frame-Options        : empêche le site d'être chargé dans une <iframe> (anti-clickjacking)
Referrer-Policy         : limite les informations envoyées dans l'en-tête Referer vers d'autres sites
Permissions-Policy      : désactive les API navigateur sensibles non utilisées (caméra, géoloc...)
python
# Middleware FastAPI ajoutant les headers de sécurité sur toutes les réponses
from starlette.middleware.base import BaseHTTPMiddleware

class SecurityHeadersMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload"
        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
        response.headers["Permissions-Policy"] = "geolocation=(), camera=(), microphone=()"
        response.headers["Content-Security-Policy"] = (
            "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; "
            "img-src 'self' data: https:; connect-src 'self' https://api.technologik.local; "
            "object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
        )
        return response

app.add_middleware(SecurityHeadersMiddleware)
text
# Anatomie d'une Content-Security-Policy (CSP)
default-src 'self'          : source par défaut pour toute ressource non explicitement listée
script-src 'self'            : scripts uniquement depuis le même domaine (bloque les scripts inline sans nonce)
style-src 'self' 'unsafe-inline' : styles depuis le domaine + inline autorisé (à éviter si possible)
img-src 'self' data: https:  : images locales, data URIs et toute source HTTPS
connect-src 'self' https://api.technologik.local : XHR/fetch/WebSocket limités à ces origines
object-src 'none'            : bloque <object>/<embed>/<applet> (vecteurs Flash historiques)
frame-ancestors 'none'       : équivalent moderne de X-Frame-Options, dans la CSP elle-même
html
<!-- CSP avec nonce pour autoriser des scripts inline spécifiques sans "unsafe-inline" global -->
<!-- Le nonce est généré aléatoirement à CHAQUE requête côté serveur -->
<script nonce="{{ csp_nonce }}">
  console.log("Ce script est autorisé car son nonce correspond au header CSP");
</script>
python
# Génération du nonce CSP par requête (FastAPI)
import secrets

@app.middleware("http")
async def csp_nonce_middleware(request: Request, call_next):
    nonce = secrets.token_urlsafe(16)
    request.state.csp_nonce = nonce
    response = await call_next(request)
    response.headers["Content-Security-Policy"] = (
        f"default-src 'self'; script-src 'self' 'nonce-{nonce}'; object-src 'none'"
    )
    return response
bash
# Vérifier les headers de sécurité d'un site (audit rapide)
curl -sI https://technologik.local | grep -Ei "strict-transport|x-frame|x-content-type|content-security"

# Outils en ligne pour un audit complet (à lancer sur ses propres domaines)
# https://securityheaders.com
# https://observatory.mozilla.org
text
# Mode "report-only" pour tester une CSP sans casser le site en production
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violation-report
# Le navigateur remonte les violations sans bloquer -> permet d'affiner la policy avant activation stricte

Résumé

  • HSTS, X-Frame-Options, X-Content-Type-Options sont des défenses simples à fort impact.
  • La CSP est la défense la plus puissante contre le XSS résiduel : restreindre script-src au strict nécessaire.
  • Préférer un nonce par requête à 'unsafe-inline' pour les scripts inline légitimes.
  • Tester en Content-Security-Policy-Report-Only avant de passer en mode bloquant strict.

Exercices pratiques

1 disponible
1

Mission : la CSP qui a bloqué le tableau de bord en production

Objectif : Diagnostiquer une CSP mal conçue à partir d'erreurs de console, puis la corriger sans réintroduire une faiblesse contre le XSS.

Contexte

Après l'activation d'une nouvelle Content-Security-Policy sur technologik.local, les utilisateurs signalent que le tableau de bord ne fonctionne plus : la console du navigateur affiche Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Le développeur qui a écrit ce script inline pour initialiser un graphique n'a pas anticipé la CSP.

Résoudre l’exercice →