mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-14 10:49:35 +02:00
Durcissement : mot de passe actuel, sessions révocables, dépendances, CSP, tests
Suite de l'audit : les cinq points laissés en suspens sont traités. Mot de passe - `changeOwnPassword` exige désormais le mot de passe actuel. Un poste laissé ouvert ne suffit plus à s'approprier un compte. Les erreurs reviennent sur l'écran avec un message au lieu d'une page d'erreur brute. Sessions révocables - Colonne `users.session_version`, portée dans le jeton et comparée à la base. - `getSession()` (src/lib/session.ts) remplace `auth()` sur les 20 pages et actions : rôle, rattachement adhérent et existence du compte sont relus à chaque requête. Supprimer un compte ou réinitialiser un mot de passe coupe immédiatement les sessions ouvertes, sans attendre l'expiration du jeton. Dépendances — de 8 vulnérabilités (2 critiques) à zéro - next 15.5.19 → 15.5.21, next-auth beta.25 → beta.32, drizzle-orm 0.38 → 0.45. - postcss et sharp forcés par `overrides` sur leurs versions corrigées, Next ne les ayant pas encore reprises ; drizzle-kit et esbuild montés côté outillage. - `npm audit fix --force` a été écarté : il proposait de RÉTROGRADER Next en 9.3.3 et eslint-config-next en 12, ce qui aurait cassé l'application. - `eslint-config-next` traînait dans node_modules sans être déclaré : retiré. CSP complète - Politique à nonce posée par le middleware, nonce régénéré à chaque requête, `script-src` sans 'unsafe-inline'. `style-src` garde 'unsafe-inline' : tout le design repose sur des attributs style, et une injection de style n'a pas la portée d'une injection de script. - Conséquence assumée : rendu dynamique pour toutes les pages, un HTML pré-généré ne pouvant pas porter de nonce. Tests - `npm test` (runner natif node:test via tsx), 18 tests sur le filtre XSS, la limitation des tentatives de connexion et le chiffrement des jetons. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
This commit is contained in:
36 files changed
+2151
-6053
No files matched your search
+52
-3
@@ -1,13 +1,62 @@
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import NextAuth from "next-auth";
|
||||
import { authConfig } from "@/auth.config";
|
||||
|
||||
// Edge-safe auth instance (no providers / db) used only to gate routes.
|
||||
// The redirect target is derived from AUTH_URL (set it to your public URL when
|
||||
// running behind a reverse proxy, e.g. AUTH_URL=https://pleinr.ffnancy.fr).
|
||||
export const { auth: middleware } = NextAuth(authConfig);
|
||||
const { auth } = NextAuth(authConfig);
|
||||
|
||||
export default middleware;
|
||||
/**
|
||||
* Politique de sécurité du contenu, avec un nonce par requête.
|
||||
*
|
||||
* Next signe ses propres scripts d'hydratation avec le nonce lu dans l'en-tête
|
||||
* CSP de la requête : c'est ce qui permet d'interdire les scripts en ligne sans
|
||||
* casser l'application.
|
||||
*
|
||||
* `style-src` garde 'unsafe-inline' : tout le design repose sur des attributs
|
||||
* `style` et sur la feuille Google Fonts. C'est un compromis assumé — une
|
||||
* injection de style est sans commune mesure avec une injection de script.
|
||||
*/
|
||||
function contentSecurityPolicy(nonce: string, isDev: boolean): string {
|
||||
return [
|
||||
"default-src 'self'",
|
||||
"base-uri 'self'",
|
||||
"object-src 'none'",
|
||||
"form-action 'self'",
|
||||
"frame-ancestors 'self'",
|
||||
// 'strict-dynamic' laisse les scripts chargés par un script de confiance
|
||||
// s'exécuter ; en développement Next a besoin d'eval pour le rafraîchissement.
|
||||
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' ${isDev ? "'unsafe-eval'" : ""}`.trim(),
|
||||
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
||||
"font-src 'self' https://fonts.gstatic.com",
|
||||
// data: pour les images stockées en data-URI, https: pour les vignettes distantes.
|
||||
"img-src 'self' data: https:",
|
||||
"connect-src 'self'",
|
||||
// Carte « Nous situer » de la fiche adhérent.
|
||||
"frame-src https://www.google.com https://maps.google.com",
|
||||
"upgrade-insecure-requests",
|
||||
].join("; ");
|
||||
}
|
||||
|
||||
export default auth((request: NextRequest) => {
|
||||
// `btoa` et non `Buffer` : le middleware tourne sur le runtime Edge.
|
||||
const nonce = btoa(crypto.randomUUID());
|
||||
const csp = contentSecurityPolicy(nonce, process.env.NODE_ENV !== "production");
|
||||
|
||||
// L'en-tête est posé sur la REQUÊTE : Next y lit le nonce pour l'appliquer à
|
||||
// ses balises <script>. Il est ensuite renvoyé sur la réponse au navigateur.
|
||||
const requestHeaders = new Headers(request.headers);
|
||||
requestHeaders.set("x-nonce", nonce);
|
||||
requestHeaders.set("content-security-policy", csp);
|
||||
|
||||
const response = NextResponse.next({ request: { headers: requestHeaders } });
|
||||
response.headers.set("content-security-policy", csp);
|
||||
return response;
|
||||
}) as unknown as (request: NextRequest) => Response | Promise<Response>;
|
||||
|
||||
export const config = {
|
||||
matcher: ["/backend/:path*"],
|
||||
// La CSP doit couvrir toutes les pages, pas seulement /backend. On exclut les
|
||||
// ressources statiques, qui n'ont pas besoin d'être traitées.
|
||||
matcher: ["/((?!_next/static|_next/image|assets|favicon.ico).*)"],
|
||||
};
|
||||
Reference in new issue
Block a user