Skip to Content
SécuritéPartie 10 — Sécurité Next.js / React / Node10.3 — Auth.js, middleware & headers

Chapitre 10.3 — Auth.js, middleware & headers

⏱️ TL;DR — Pour l’authentification en Next.js, on utilise une bibliothèque éprouvée — Auth.js (ex-NextAuth) — plutôt que de rouler la sienne : elle gère OAuth/OIDC, sessions, cookies sécurisés. Le middleware Next.js est pratique pour rediriger les non-connectés, mais ce n’est pas une barrière de sécurité suffisante : il s’exécute en amont (edge), ne « voit » pas tout, et peut être contourné selon la config. La vraie protection reste dans chaque Server Action / route handler / Server Component (vérifier la session au point d’accès aux données — chapitre 10.2). Enfin, on pose les en-têtes de sécurité (CSP, HSTS, frame-ancestors…) via next.config (headers()) ou le middleware.

🎯 Objectifs

  • Utiliser Auth.js pour l’authentification (sessions, OAuth/OIDC, cookies sûrs).
  • Comprendre le rôle et les limites du middleware Next.js (pas une barrière suffisante).
  • Placer la vraie vérification d’accès au point d’accès aux données.
  • Poser les en-têtes de sécurité via next.config/middleware.

Auth.js : ne roule pas ta propre auth

L’authentification est difficile à faire correctement (Partie 4) : sessions, cookies sécurisés, OAuth/OIDC avec state/PKCE, hachage, protection CSRF. En Next.js, la solution recommandée est une bibliothèque mûre — Auth.js / NextAuth — qui intègre ces bonnes pratiques :

  • Sessions via cookie HttpOnly + Secure + SameSite (ou JWT selon la stratégie — préférer la session côté serveur quand la révocation compte, chapitre 4.1).
  • OAuth/OIDC (« se connecter avec Google/GitHub ») avec state/PKCE gérés (chapitre 4.4).
  • Helpers pour lire la session côté serveur (auth()), protéger les routes, etc.
// Lire la session cote serveur (Server Component / Server Action / route handler) import { auth } from '@/lib/auth' const session = await auth() if (!session) { /* non authentifie */ }

On configure strictement (providers, cookies sécurisés, callbacks qui ne fuient pas de données sensibles dans le token) et on ne réimplémente pas le flux d’auth soi-même.

Le middleware n’est pas une barrière suffisante

Le middleware Next.js (middleware.ts) s’exécute avant le rendu d’une route et sert souvent à rediriger les utilisateurs non connectés (vérifier un cookie, renvoyer vers /login). C’est utile pour l’UX et une première couche — mais pas une protection de sécurité suffisante :

  • Il s’exécute dans un runtime edge limité, souvent avant l’accès réel aux données, et ne « voit » pas toujours le contexte complet.
  • Sa portée dépend du matcher : une route non couverte (oubli dans la config) n’est pas protégée.
  • Il peut, selon les versions et configurations, être contourné ; historiquement, des failles ont montré qu’on ne doit pas en faire l’unique gardien de l’accès.
// middleware.ts : OK pour rediriger (UX), PAS suffisant comme seule protection export function middleware(req) { if (!hasSessionCookie(req)) return NextResponse.redirect(new URL('/login', req.url)) } export const config = { matcher: ['/dashboard/:path*'] } // attention : ne couvre QUE ce matcher

La vraie protection doit se trouver au point d’accès aux données : dans chaque Server Component qui lit des données sensibles, chaque Server Action et chaque route handler (chapitre 10.2), on revérifie la session et l’autorisation par objet. Le middleware améliore l’UX (rediriger tôt) mais ne remplace pas ces contrôles.

⚠️ Piège — Faire du middleware la seule barrière d’autorisation est une erreur classique et a déjà mené à des contournements réels. Un attaquant qui appelle directement une Server Action, un route handler ou une route non couverte par le matcher échappe au middleware. La règle (cohérente avec toute la Partie 5) : vérifier l’accès là où la donnée est lue/écrite, pas seulement en amont. Le middleware est une commodité, pas un rempart. « Protégé par le middleware » ne veut pas dire « protégé ».

💡 Réflexe — Considère le middleware comme un portier d’accueil (il oriente, améliore l’UX) et non comme le coffre-fort (le vrai contrôle). Pour chaque donnée sensible, pose-toi : « le code qui lit/écrit cette donnée vérifie-t-il lui-même la session et l’autorisation ? ». Si la réponse dépend uniquement du middleware, c’est insuffisant. Le contrôle vit au plus près de la donnée.

Les en-têtes de sécurité en Next.js

On pose les en-têtes de sécurité (Partie 2 : CSP, HSTS, X-Content-Type-Options, frame-ancestors, Referrer-Policy) via la fonction headers() de next.config, ou via le middleware, ou au niveau du reverse proxy (Serveur Linux) :

// next.config.js : en-tetes de securite globaux module.exports = { async headers() { return [{ source: '/:path*', headers: [ { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'X-Frame-Options', value: 'DENY' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains' }, // CSP : de preference avec nonce (via middleware) — voir Partie 7 ], }] }, }

Pour une CSP à nonce (recommandée, chapitre 7.4), on la génère souvent dans le middleware (nonce aléatoire par requête, injecté dans l’en-tête CSP et transmis au rendu). Next.js documente ce pattern. On déploie la CSP en Report-Only d’abord, puis en mode bloquant.

🎯 Côté attaquant — Sur une app Next.js, l’attaquant teste : appeler les Server Actions / route handlers directement (le middleware ne les protège pas forcément), énumérer les routes non couvertes par le matcher, et regarder si l’autorisation est faite au point d’accès ou seulement en amont. Il vérifie aussi l’absence d’en-têtes (frame-ancestors → clickjacking, CSP faible → XSS). Une app qui « fait confiance » au middleware pour l’accès et néglige les contrôles au niveau des données est une cible facile.

🧭 Sur FormaCampus — FormaCampus utilise Auth.js (sessions en cookie HttpOnly/Secure/SameSite, OIDC pour le SSO des établissements avec state/PKCE gérés). Le middleware redirige les non-connectés (UX) mais chaque Server Component sensible, Server Action et route handler revérifie la session et l’autorisation par objet (chapitre 10.2) — le middleware n’est jamais l’unique gardien. Les en-têtes de sécurité sont posés au reverse proxy et via next.config, avec une CSP à nonce générée dans le middleware, déployée en Report-Only puis bloquante. Cette double approche (redirection UX + contrôle au point de donnée) résiste aux appels directs d’endpoints.

✏️ Exercices

Exercice 1 — Le middleware trompeur. Une app protège /dashboard via le middleware (redirection si pas de cookie). Un route handler app/api/factures/[id]/route.ts renvoie une facture, sans vérifier la session (« c’est derrière le dashboard »). Où est la faille ?

✅ Solution

La faille : le route handler n’est pas protégé par le middleware du dashboard (matcher différent, et de toute façon un route handler s’appelle directement). Un attaquant appelle GET /api/factures/123 sans passer par /dashboard : aucune vérification de session ni d’appartenance → il lit n’importe quelle facture (IDOR + accès non authentifié). Le middleware ne « couvre » pas ce endpoint, et même s’il le couvrait, on ne doit pas s’y fier seul. Correctif : dans le route handler, vérifier la session (await auth(), 401 sinon) et l’autorisation par objet (facture appartenant à l’utilisateur — requête portée par le propriétaire, 403/404 sinon). Le contrôle vit au point d’accès à la donnée, pas seulement dans le middleware.

Exercice 2 — Place les en-têtes. Cite deux façons de poser des en-têtes de sécurité en Next.js et l’en-tête à privilégier pour empêcher le clickjacking.

✅ Solution

Deux façons : (1) la fonction headers() dans next.config.js (en-têtes statiques globaux) ; (2) le middleware (utile pour une CSP à nonce générée par requête) ; (on peut aussi les poser au reverse proxy). Pour le clickjacking, on privilégie X-Frame-Options: DENY et/ou la directive CSP frame-ancestors 'none' (moderne, prioritaire), qui interdisent la mise en iframe du site par des tiers (Partie 8).

🧠 Quiz de révision

1. Pourquoi utiliser Auth.js plutôt que sa propre auth ?

Parce que l’authentification est difficile à faire correctement (sessions, cookies sûrs, OAuth/OIDC avec state/PKCE, CSRF). Auth.js intègre ces bonnes pratiques éprouvées ; on le configure strictement au lieu de réimplémenter un flux à la main (source classique de failles).

2. Le middleware Next.js est-il une barrière de sécurité suffisante ?

Non : il est utile pour rediriger (UX) et comme première couche, mais il s’exécute en amont (edge), sa portée dépend du matcher (routes oubliées non protégées), et il peut être contourné (appels directs d’endpoints). La vraie protection est au point d’accès aux données.

3. Où doit se trouver la vraie vérification d’accès ?

Au point d’accès aux données : dans chaque Server Component sensible, Server Action et route handler, on revérifie la session et l’autorisation par objet (chapitre 10.2). Le middleware améliore l’UX mais ne remplace pas ces contrôles.

4. Comment pose-t-on les en-têtes de sécurité en Next.js ?

Via la fonction headers() de next.config (en-têtes globaux), via le middleware (notamment pour une CSP à nonce par requête), ou au reverse proxy. On déploie la CSP en Report-Only d’abord.

5. Quel est le risque de se fier uniquement au middleware pour l’autorisation ?

Un attaquant contourne le middleware en appelant directement une Server Action, un route handler ou une route non couverte par le matcher → il accède aux données sans le contrôle. Il faut vérifier l’accès là où la donnée est lue/écrite, pas seulement en amont.


Chapitre suivant : Node, npm & SSRF — sécuriser le runtime : dépendances npm (audit, supply chain) et SSRF via les fetch côté serveur.

Last updated on