Skip to Content

Chapitre 10.6 — 2FA & durcissement

Partie 10 : Sécurité — Chapitre 6/6 Version de référence : Symfony 7.4.

⏱️ TL;DR

  • La 2FA (double authentification) ajoute une seconde preuve : TOTP (appli type Google Authenticator) ou code par email. Bundle : scheb/2fa-bundle.
  • Le rate limiting protège des attaques par force brute : login_throttling (natif sur le firewall) + le composant RateLimiter pour l’API.
  • Les en-têtes de sécurité (CSP, HSTS, X-Frame-Options) durcissent le navigateur (nelmio/security-bundle).
  • Contre les mots de passe faibles/compromis : #[Assert\NotCompromisedPassword] et une politique de longueur.
  • Checklist OWASP : injection (paramètres Doctrine), XSS (échappement Twig / HtmlSanitizer), CSRF (formulaires), auth (hachage/2FA), contrôle d’accès (voters), secrets (coffre), HTTPS, dépendances (composer audit).
  • Pour un dev Next.js : mêmes préoccupations que côté JS (Helmet, rate-limit, 2FA), mais intégrées et outillées côté Symfony.

🎯 Objectifs

  • Ajouter la 2FA (TOTP/email).
  • Limiter les tentatives (login throttling, rate limiter).
  • Poser des en-têtes de sécurité et vérifier les mots de passe.
  • Dérouler une checklist OWASP appliquée à BookLab.

1. La double authentification (2FA)

Un mot de passe ne suffit plus pour les comptes sensibles (admins). La 2FA exige une seconde preuve :

composer require scheb/2fa-bundle scheb/2fa-totp
  • TOTP : un code à 6 chiffres généré par une appli (Google/Microsoft Authenticator, Authy) à partir d’un secret partagé (QR code). Le plus sûr, hors ligne.
  • Email : un code envoyé par email à chaque connexion. Plus simple, moins fort (dépend de la boîte mail).

Le flux : après la connexion par mot de passe réussie, l’utilisateur est mis dans un état « en attente de 2FA » — il doit saisir son code avant d’accéder à l’application. Le bundle gère cet état intermédiaire et le firewall.

2. Rate limiting : contrer la force brute

Login throttling (natif)

Le firewall Symfony sait limiter les tentatives de connexion échouées, nativement :

firewalls: main: login_throttling: max_attempts: 5 # 5 essais / minute par IP+identifiant interval: '1 minute'

Au-delà, les tentatives sont bloquées temporairement — ça neutralise le brute-force sur le login.

RateLimiter pour l’API

Pour limiter n’importe quel endpoint (API, inscription, envoi d’email), le composant RateLimiter :

composer require symfony/rate-limiter
# config/packages/rate_limiter.yaml framework: rate_limiter: api: policy: 'sliding_window' limit: 100 interval: '1 minute'
public function __invoke(Request $request, RateLimiterFactory $apiLimiter): Response { $limiter = $apiLimiter->create($request->getClientIp()); if (!$limiter->consume()->isAccepted()) { return new JsonResponse(['error' => 'Trop de requêtes'], 429); // 429 Too Many Requests } // ... }

3. En-têtes de sécurité

Les en-têtes HTTP de sécurité durcissent le comportement du navigateur :

  • CSP (Content-Security-Policy) : restreint d’où peuvent venir scripts/styles/images → forte protection anti-XSS.
  • HSTS (Strict-Transport-Security) : force HTTPS.
  • X-Frame-Options / frame-ancestors : empêche le clickjacking (mise en iframe).
  • X-Content-Type-Options: nosniff.

On les configure avec nelmio/security-bundle (ou un listener sur kernel.response). La CSP demande un réglage soigné (elle peut casser des scripts légitimes) mais c’est l’une des protections les plus efficaces.

4. Mots de passe robustes

  • Longueur minimale : #[Assert\Length(min: 12)] pour les comptes sensibles.
  • Mots de passe compromis : #[Assert\NotCompromisedPassword] vérifie (via l’API Have I Been Pwned, en k-anonymat) que le mot de passe n’apparaît pas dans des fuites connues — sans jamais l’envoyer en clair.
#[Assert\NotBlank] #[Assert\Length(min: 12)] #[Assert\NotCompromisedPassword] public string $plainPassword = '';

5. La checklist OWASP appliquée à BookLab

Récapitulatif des protections vues dans le cours, mappées sur les grands risques :

Risque OWASPProtection dans BookLabChapitre
Injection SQLParamètres Doctrine (setParameter), jamais de concat6.3
XSSÉchappement Twig auto + HtmlSanitizer pour le HTML utilisateur7.1
CSRFToken automatique sur les formulaires (session)9.1
Auth casséeHachage auto, 2FA, login throttling10.3, 10.6
Contrôle d’accès défaillantFirewalls + access_control + voters (défense en profondeur)10.1-10.2
Mauvaise config / secretsCoffre de secrets, .env.local non committé, /admin protégé3.4, 7.5
Composants vulnérablescomposer audit (CI, Partie 14)14
TransportHTTPS (HSTS), cookies Secure/httpOnly/SameSite10.4
Upload malveillantValidation MIME, nom régénéré, stockage non exécutable9.5

⚠️ Piège : composer audit vérifie les vulnérabilités connues de vos dépendances. À lancer en CI (Partie 14) et régulièrement — une faille dans une lib tierce est aussi grave qu’une faille dans votre code. Ne « gelez » jamais des dépendances vulnérables sous prétexte que « ça marche ».

💡 Pour un dev Next.js : cette checklist est transposable à tout backend. Côté Symfony, la plupart de ces protections sont par défaut (échappement Twig, CSRF des formulaires, paramètres Doctrine) ou déclaratives (voters, rate limiting) — moins de risque d’oubli qu’en assemblant soi-même un backend Express. C’est un argument de robustesse en faveur de Symfony pour un backend d’API sérieux.

✏️ Exercices

Exercice 1. Activez le login throttling à 5 tentatives par minute sur le back-office. Où, et quel effet ?

✅ Solution

Dans security.yaml, sous le firewall main :

login_throttling: max_attempts: 5 interval: '1 minute'

Effet : au-delà de 5 tentatives de connexion échouées par minute (pour un couple IP/identifiant), les tentatives sont bloquées temporairement — ce qui neutralise les attaques par force brute sur le formulaire de connexion.

Exercice 2. Pourquoi TOTP est-il généralement plus sûr que la 2FA par email ?

✅ Solution

TOTP génère le code localement (appli), hors ligne, à partir d’un secret partagé — rien à intercepter sur le réseau, et indépendant d’un autre compte. La 2FA par email dépend de la sécurité de la boîte mail (si elle est compromise, la 2FA l’est aussi) et transite par le réseau/serveurs mail. TOTP réduit la surface d’attaque.

Exercice 3. Citez trois protections OWASP actives par défaut dans une app Symfony bien configurée.

✅ Solution

Au choix : l’échappement automatique de Twig (anti-XSS), le token CSRF automatique des formulaires du composant Form, les paramètres Doctrine (anti-injection SQL) quand on utilise setParameter. (À compléter par les protections déclaratives : voters, rate limiting, en-têtes de sécurité, hachage auto.)

🧠 Quiz de révision

1. Quelles sont les deux formes de 2FA courantes ?

TOTP (code d’une appli, hors ligne, plus sûr) et email (code envoyé par email, plus simple). Via scheb/2fa-bundle.

2. Comment limite-t-on les tentatives de connexion ?

Avec login_throttling (natif sur le firewall) ; pour l’API, avec le composant RateLimiter (renvoie 429 au-delà du seuil).

3. Quel en-tête protège le plus efficacement du XSS ?

La CSP (Content-Security-Policy), qui restreint les sources de scripts/styles (via nelmio/security-bundle).

4. Comment refuser un mot de passe déjà compromis ?

Avec #[Assert\NotCompromisedPassword] (vérifie via Have I Been Pwned en k-anonymat, sans envoyer le mot de passe en clair).

5. Quelle commande vérifie les dépendances vulnérables ?

composer audit (à intégrer en CI, Partie 14).


Fin de la Partie 10. BookLab est sécurisé : firewalls (session + JWT), rôles et voters, hachage, JWT pour l’API, OAuth, 2FA et durcissement. L’API est prête à être ouverte au front Next.js — c’est tout l’objet de la partie suivante, le cœur premium du cours.

Prochaine étape : Partie 11 — API Platform & API headless pour Next.js — exposer BookLab en API et la consommer depuis Next.js.