Skip to Content

Chapitre 15.6 — Durcissement production

Partie 15 : Expert — Chapitre 6/9 Version de référence : Symfony 7.4.

⏱️ TL;DR

  • Le durcissement production rassemble et applique les protections vues au fil du cours, en une checklist opérationnelle.
  • Rate limiting (RateLimiter) sur les endpoints sensibles ; en-têtes de sécurité (CSP, HSTS) via nelmio/security-bundle ; secrets dans le coffre chiffré.
  • HTTPS partout, cookies Secure/HttpOnly/SameSite ; CORS restreint (Partie 11.6).
  • composer audit en CI (dépendances) ; HtmlSanitizer pour tout HTML utilisateur.
  • Le durcissement n’est pas une étape finale mais une posture : par défaut sécurisé, moindre privilège, défense en profondeur.
  • Pour un dev Next.js : c’est votre checklist Helmet/rate-limit/secrets, appliquée au backend — et coordonnée avec le front.

🎯 Objectifs

  • Appliquer le rate limiting là où il compte.
  • Poser les en-têtes de sécurité (CSP/HSTS).
  • Gérer les secrets et le transport de façon sûre.
  • Dérouler une checklist de durcissement prod.

1. Rate limiting

Protéger les endpoints sensibles ou coûteux des abus (brute-force, scraping, déni de service applicatif). Rappel (Partie 10.6/12.2) : login_throttling natif pour le login, et le composant RateLimiter pour le reste (API, inscription, envoi d’e-mails).

public function resend(Request $request, RateLimiterFactory $emailResendLimiter): Response { $limiter = $emailResendLimiter->create($request->getClientIp()); if (!$limiter->consume()->isAccepted()) { return new JsonResponse(['error' => 'Trop de tentatives'], 429); } // ... }

On applique le rate limiting par IP, par utilisateur, ou par clé d’API selon le contexte. Objectif : un attaquant ne doit pas pouvoir marteler un endpoint.

2. Les en-têtes de sécurité

Rappel (Partie 10.6), en insistant sur la prod :

  • CSP (Content-Security-Policy) : la protection anti-XSS de fond. Restreint d’où viennent scripts/styles/images. Nécessite un réglage soigné (elle peut casser des scripts légitimes) — on la construit progressivement (mode report-only d’abord, pour voir ce qu’elle bloquerait, puis on durcit).
  • HSTS (Strict-Transport-Security) : force le navigateur à n’utiliser que HTTPS.
  • X-Content-Type-Options: nosniff, X-Frame-Options / frame-ancestors (anti-clickjacking), Referrer-Policy.

Via nelmio/security-bundle ou un listener sur kernel.response. Ces en-têtes durcissent le navigateur — une couche gratuite et efficace.

3. Secrets et transport

  • Secrets : dans le coffre chiffré (Partie 3.4), jamais en clair, jamais committés. La clé de déchiffrement de prod est déployée séparément (variable d’env sécurisée).
  • HTTPS partout : redirection HTTP→HTTPS, HSTS. Aucune donnée en clair.
  • Cookies : Secure (HTTPS only), HttpOnly (pas de JS, anti-XSS), SameSite=Lax/Strict (anti-CSRF). Rappel : le JWT du front Next.js va en cookie httpOnly (Partie 11.8).

⚠️ Piège config prod : déployer avec APP_ENV=dev ou APP_DEBUG=1 est une faille majeure : le profiler s’expose, les erreurs révèlent des détails internes (chemins, requêtes, config), et les performances chutent. Toujours APP_ENV=prod APP_DEBUG=0 en production, et vérifier que /_profiler n’est pas accessible. C’est l’une des erreurs de config les plus courantes — et les plus graves.

4. HTML utilisateur : HtmlSanitizer

Si vous acceptez du HTML de l’utilisateur (un éditeur riche pour une description), ne l’affichez jamais en |raw brut (XSS, Partie 7.1). Nettoyez-le avec le composant HtmlSanitizer : il ne garde qu’une allowlist de balises/attributs sûrs.

use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; $safeHtml = $sanitizer->sanitize($userProvidedHtml); // supprime <script>, on* handlers, etc.

5. La checklist de durcissement prod

Un récapitulatif opérationnel, à passer avant chaque mise en prod :

  • APP_ENV=prod, APP_DEBUG=0, profiler inaccessible.
  • HTTPS forcé (+ HSTS), cookies Secure/HttpOnly/SameSite.
  • Secrets dans le coffre, aucun secret committé, clé de prod déployée à part.
  • CSP active (au moins report-only, idéalement enforce), en-têtes de sécurité posés.
  • CORS restreint aux origines connues (pas de * avec credentials, Partie 11.6).
  • Rate limiting sur login, inscription, endpoints coûteux.
  • Contrôle d’accès vérifié côté serveur (voters, access_control) — défense en profondeur.
  • Requêtes paramétrées (Doctrine), échappement Twig, CSRF sur les formulaires.
  • Uploads validés (MIME, nom régénéré, stockage non exécutable).
  • composer audit vert en CI (dépendances sans vulnérabilité connue).
  • HtmlSanitizer sur tout HTML utilisateur.
  • Logs sans données sensibles ; Sentry branché.
  • Sauvegardes de la base testées (et restaurables !).

💡 Pour un dev Next.js : cette checklist est le pendant backend de votre durcissement front (Helmet, CSP, secrets Vercel, rate-limit). L’atout d’un backend Symfony : beaucoup de ces protections sont par défaut ou déclaratives (échappement Twig, CSRF, paramètres Doctrine, voters) — moins de risque d’oubli qu’en assemblant un backend Express à la main. Sur BookLab, le durcissement se coordonne entre les deux : CORS côté Symfony + cookies httpOnly posés par Next, CSP cohérente des deux côtés.

✏️ Exercices

Exercice 1. Quelle est l’erreur de configuration de production la plus grave et la plus courante ?

✅ Solution

Déployer en APP_ENV=dev / APP_DEBUG=1. Conséquences : le profiler et les pages d’erreur détaillées exposent la config, les chemins, les requêtes SQL (aide un attaquant), et les performances s’effondrent (recompilation, instrumentation). Il faut toujours APP_ENV=prod APP_DEBUG=0 en production et vérifier que /_profiler est inaccessible.

Exercice 2. Comment déployer une CSP sans casser le site ?

✅ Solution

Progressivement : commencer en mode report-only (Content-Security-Policy-Report-Only), qui signale ce qui serait bloqué sans bloquer réellement. On analyse les rapports, on ajuste la politique (sources autorisées) jusqu’à ce qu’elle ne casse aucun script légitime, puis on passe en mode enforce. Déployer une CSP stricte d’un coup casse souvent des scripts/styles légitimes — d’où l’approche itérative.

Exercice 3. Vous acceptez une description en HTML riche pour un service. Comment l’afficher sans risque XSS ?

✅ Solution

Nettoyer le HTML avec le composant HtmlSanitizer ($sanitizer->sanitize($html)) qui ne conserve qu’une allowlist de balises/attributs sûrs (supprime <script>, les handlers on*, etc.), puis afficher le résultat assaini (éventuellement en |raw). Jamais afficher le HTML utilisateur brut en |raw (XSS). Le sanitizer fait le pont entre « autoriser un peu de mise en forme » et « rester sûr ».

🧠 Quiz de révision

1. Que protège le rate limiting ?

Les endpoints sensibles/coûteux contre les abus (brute-force, scraping, DoS applicatif) — par IP/utilisateur/clé, avec un 429 au-delà du seuil.

2. Quel en-tête est la protection anti-XSS de fond, et comment le déployer ?

La CSP ; on la déploie progressivement (report-only → analyse → enforce) pour ne pas casser les scripts légitimes.

3. Quelle config est impérative en production ?

APP_ENV=prod, APP_DEBUG=0, profiler inaccessible — sinon fuite d’infos et perfs dégradées.

4. Comment afficher du HTML utilisateur sans XSS ?

En le nettoyant avec HtmlSanitizer (allowlist), jamais en |raw brut.

5. Le durcissement est-il une étape finale ?

Non : c’est une posture continue (par défaut sécurisé, moindre privilège, défense en profondeur), appliquée via une checklist avant chaque mise en prod.


Prochain chapitre : 15.7 — DDD, CQRS & hexagonal — structurer un domaine complexe.