Skip to Content

Chapitre 14.1 — TLS, headers & HSTS en prod

⏱️ TL;DR — En production, on centralise la sécurité du transport et des en-têtes au reverse proxy (Nginx — cours Serveur Linux) : HTTPS partout (TLS 1.2/1.3, certificat Let’s Encrypt), redirection HTTP → HTTPS, HSTS pour forcer le HTTPS aux visites suivantes, et les en-têtes de sécurité (CSP, nosniff, frame-ancestors, Referrer-Policy…) posés une fois sur toutes les réponses. Le proxy est le bon endroit : cohérent, indépendant de l’appli, appliqué à tout ce qui passe. C’est la mise en œuvre concrète, côté infra, des Parties 2 (en-têtes) et de la « sécurité du transport ».

🎯 Objectifs

  • Configurer HTTPS partout et rediriger HTTP → HTTPS au proxy.
  • Activer HSTS correctement (durée, sous-domaines, preload).
  • Poser les en-têtes de sécurité au reverse proxy (une fois, pour tout).
  • Comprendre pourquoi le proxy est le bon endroit pour ces couches.

Le reverse proxy : le bon endroit

En prod, la plupart des applis sont derrière un reverse proxy (Nginx typiquement — voir le cours Serveur Linux) : il reçoit le trafic, termine le TLS, et relaie vers l’appli. C’est l’endroit idéal pour appliquer la sécurité du transport et les en-têtes :

  • Cohérence : appliqué à toutes les réponses, indépendamment de la techno de l’appli (Next.js, Symfony, statique).
  • Une seule configuration à maintenir, plutôt que de dupliquer dans chaque appli.
  • Séparation : l’appli se concentre sur sa logique ; le proxy gère TLS, HSTS, en-têtes, redirection.

On peut aussi poser ces en-têtes dans l’appli (Parties 9-10) — les deux ne sont pas exclusifs — mais le proxy garantit une couche uniforme et toujours présente.

HTTPS partout + redirection

Rappel Partie 2 : HTTPS est le sol de la sécurité. En prod :

  • Certificat valide : Let’s Encrypt (gratuit, 90 jours, renouvellement automatique via Certbot — Serveur Linux).
  • TLS 1.2 / 1.3 uniquement : désactiver SSL et TLS 1.0/1.1 (obsolètes), choisir des suites cryptographiques sûres (s’appuyer sur le Mozilla SSL Configuration Generator).
  • Rediriger HTTP → HTTPS : tout accès en http:// est redirigé vers https://, pour qu’aucune requête ne reste en clair.
# Rediriger tout le HTTP vers HTTPS server { listen 80; server_name app.formacampus.fr; return 301 https://$host$request_uri; # redirection permanente vers HTTPS }

HSTS : forcer le HTTPS ensuite

La redirection HTTP → HTTPS laisse une petite fenêtre : la toute première requête en http:// peut être interceptée avant la redirection. HSTS (Partie 2) ferme cette fenêtre en demandant au navigateur de n’utiliser que HTTPS pour ce domaine, pendant une durée donnée.

# Dans le server block HTTPS add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
  • max-age : durée de mémorisation (ici ~2 ans).
  • includeSubDomains / preload : à n’activer que si tous les sous-domaines sont (et resteront) en HTTPS (rappel du piège Partie 2 — le preload est durable et difficile à retirer). Commencer avec un max-age court, vérifier, puis augmenter.

Les en-têtes de sécurité au proxy

On pose l’ensemble des en-têtes de la Partie 2 sur toutes les réponses, au proxy :

# En-tetes de securite (dans le server block HTTPS) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always; # CSP : de preference avec nonce (souvent gere par l'appli) — voir Partie 7 # add_header Content-Security-Policy "default-src 'self'; ..." always;
  • La CSP (Partie 7) est souvent gérée par l’appli quand elle utilise des nonces (générés par requête) ; sinon, une CSP statique peut être posée au proxy. On la déploie en Report-Only d’abord.
  • Le drapeau always (Nginx) garantit que l’en-tête est ajouté même sur les réponses d’erreur.
  • Vérifier le résultat avec un outil comme securityheaders.com ou SSL Labs (note TLS).

⚠️ Piège — Deux erreurs fréquentes au proxy : (1) oublier le always sur add_header → les en-têtes manquent sur les réponses d’erreur (4xx/5xx), justement là où une fuite d’info peut arriver ; (2) dupliquer ou écraser les en-têtes entre l’appli et le proxy de façon incohérente (Nginx add_header dans un bloc enfant peut remplacer ceux du parent). Vérifie le résultat réel (les en-têtes reçus par le navigateur), pas seulement la config, et assure-toi qu’il n’y a ni oubli ni doublon contradictoire.

💡 Réflexe — Centralise TLS + redirection + HSTS + en-têtes au reverse proxy : une seule config, appliquée à tout, toujours présente. Puis vérifie empiriquement (curl -I, securityheaders.com, SSL Labs) que les en-têtes et le TLS sont bien ceux attendus sur toutes les routes (y compris erreurs). La sécurité du transport et des en-têtes doit être un acquis d’infra, pas quelque chose que chaque appli réinvente.

🎯 Côté attaquant — L’attaquant vérifie ta posture transport/en-têtes en quelques secondes (SSL Labs, curl -I, securityheaders.com) : TLS obsolète (TLS 1.0), pas de redirection HTTP → HTTPS (interception possible), pas de HSTS (fenêtre de première requête), en-têtes manquants (frame-ancestors absent → clickjacking, CSP faible → XSS, nosniff absent). Une config proxy laxiste lui donne des prises gratuites avant même de toucher à l’appli. Une config durcie et uniforme lui en refuse.

🧭 Sur FormaCampus — Le reverse proxy Nginx de FormaCampus centralise tout : HTTPS partout (Let’s Encrypt auto-renouvelé, TLS 1.2/1.3 seulement, suites sûres), redirection HTTP → HTTPS, HSTS avec includeSubDomains (tous les sous-domaines en HTTPS) et les en-têtes de sécurité sur toutes les réponses (avec always) : nosniff, X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy, et une CSP à nonce (gérée par l’appli, Report-Only puis bloquante). La note SSL Labs et securityheaders.com sont vérifiées à chaque changement d’infra. Cette couche uniforme, indépendante du code applicatif, est un acquis pour front Next.js, API Symfony et Moodle à la fois.

✏️ Exercices

Exercice 1 — Ferme la fenêtre. Un site redirige HTTP → HTTPS mais n’a pas HSTS. Quelle petite fenêtre d’attaque reste ouverte, et comment la fermer ?

✅ Solution

Sans HSTS, la toute première requête d’un utilisateur (qui tape app.formacampus.fr ou clique un lien en http://) part en clair vers le port 80 avant de recevoir la redirection 301 vers HTTPS. Un attaquant en position d’interception (Wi-Fi public, réseau compromis) peut capter ou détourner cette première requête HTTP avant la redirection (attaque de type SSL stripping). HSTS ferme cette fenêtre : Strict-Transport-Security: max-age=... demande au navigateur de n’utiliser que HTTPS pour ce domaine lors des visites suivantes (le navigateur passe directement en https:// sans jamais tenter http://). Pour les nouveaux visiteurs, le preload (inscription dans les navigateurs) force le HTTPS dès la première visite — à activer seulement si tous les sous-domaines sont en HTTPS. On ajoute donc l’en-tête HSTS au proxy, avec un max-age progressif.

Exercice 2 — Le always oublié. Pourquoi est-il important d’ajouter always aux add_header de sécurité dans Nginx ?

✅ Solution

Sans always, Nginx n’ajoute l’en-tête que sur certaines réponses (typiquement les 2xx/3xx « normales ») et pas sur les réponses d’erreur (4xx, 5xx). Or les pages d’erreur sont précisément des endroits où l’on veut aussi la protection : une page 404/500 doit elle aussi avoir nosniff, X-Frame-Options, HSTS, etc. — et elle peut par ailleurs fuiter de l’info. Avec always, l’en-tête est ajouté quelle que soit la réponse. On vérifie ensuite empiriquement (par ex. curl -I sur une URL renvoyant une erreur) que les en-têtes sont bien présents partout.

🧠 Quiz de révision

1. Pourquoi centraliser TLS et en-têtes au reverse proxy ?

Pour la cohérence (appliqué à toutes les réponses, quelle que soit l’appli), une seule config à maintenir, et la séparation (l’appli fait sa logique, le proxy gère transport/en-têtes). C’est une couche uniforme et toujours présente, complémentaire des en-têtes applicatifs.

2. Que doit-on configurer pour « HTTPS partout » ?

Un certificat valide (Let’s Encrypt, auto-renouvelé), TLS 1.2/1.3 uniquement (désactiver SSL/TLS 1.0/1.1, suites sûres), et une redirection HTTP → HTTPS (301) pour qu’aucune requête ne reste en clair.

3. Quelle fenêtre HSTS ferme-t-il ?

La fenêtre de la première requête en http:// (avant la redirection), interceptable (SSL stripping). HSTS demande au navigateur de n’utiliser que HTTPS pour le domaine ensuite ; le preload force même le HTTPS dès la première visite (si tous les sous-domaines sont en HTTPS).

4. Pourquoi le drapeau always sur add_header (Nginx) ?

Pour que les en-têtes de sécurité soient ajoutés même sur les réponses d’erreur (4xx/5xx), et pas seulement sur les réponses normales. Sans always, les pages d’erreur seraient sans protection.

5. Comment vérifier sa posture transport/en-têtes ?

Empiriquement : curl -I (voir les en-têtes reçus, y compris sur les erreurs), securityheaders.com (audit des en-têtes), SSL Labs (note TLS). On vérifie le résultat réel sur toutes les routes, pas seulement la config — pour éviter oublis et doublons contradictoires.


Chapitre suivant : Rate limiting, WAF & fail2ban — se protéger de l’abus, du volume et des bots hostiles.

Last updated on