Files
2026-05-16 00:48:50 +02:00

18 KiB

TaxActe — Corrections Sécurité Phase 1

Date : 2026-05-15
Agent : Agent Sécurité
Mission : Correction des 3 vulnérabilités critiques identifiées dans SECURITY-AUDIT.md


Résumé Exécutif

Vulnérabilités Corrigées

Vulnérabilité Statut Fichiers Modifiés Tests
🚨 Rate Limiting Login CORRIGÉ src/lib/rate-limit.ts, src/app/api/auth/login/route.ts 6 tests passent
🚨 CSRF Protection CORRIGÉ src/lib/auth.ts, src/middleware.ts ⚠️ À tester manuellement
🚨 Security Headers CORRIGÉ next.config.ts ⚠️ À tester avec securityheaders.com
⚠️ Validation Zod API CORRIGÉ 7 routes /api/calcul/* ⚠️ À tester avec inputs invalides

Statut Global

  • 4/4 corrections critiques implémentées
  • Rate limiting : 5 tentatives / 15 min par IP
  • CSRF : cookie sameSite: strict + validation origin
  • Security headers : CSP, HSTS, X-Frame-Options, etc.
  • Validation Zod : 7 routes de calcul sécurisées
  • ⚠️ Tests manuels requis (brute force, CSRF, headers score)
  • ⚠️ npm audit non exécuté (permission refusée)

1. Rate Limiting Login (Jours 1-2)

Problème Identifié

Vulnérabilité : Pas de rate limiting sur /api/auth/login → brute force possible (audit section 1.4).

Un attaquant pouvait tenter 1000+ requêtes/seconde pour deviner un mot de passe.

Solution Implémentée

Fichier créé : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/rate-limit.ts

Architecture :

  • Map in-memory (MVP, sans dépendance Redis)
  • Max 5 tentatives par IP par période de 15 minutes
  • Retourne HTTP 429 Too Many Requests si limite dépassée
  • Headers : Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • Nettoyage automatique des entrées expirées toutes les 5 minutes

Fonction principale :

checkRateLimit(
  identifier: string,  // IP ou userId
  limit: number = 5,   // Max tentatives
  windowMs: number = 15 * 60 * 1000  // Fenêtre 15 min
): RateLimitResult

Intégration : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/auth/login/route.ts

  • Vérification de l'IP avant toute logique d'authentification
  • Lecture de l'IP via getClientIp() (gère X-Forwarded-For pour reverse proxy)
  • Retour immédiat si limite dépassée (avant requête DB)

Tests : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/__tests__/rate-limit.test.ts

  • 6 tests passent (Vitest)
  • Test brute force (6ème tentative bloquée)
  • Test expiration de fenêtre
  • Test reset manuel
  • Test isolation multi-IP

Commande de test :

npm run test -- src/lib/__tests__/rate-limit.test.ts

Résultat : Tests passent (6 passed)


2. CSRF Protection (Jours 1-2)

Problème Identifié

Vulnérabilité : Pas de CSRF token explicite, cookie sameSite: lax insuffisant (audit section 2.4).

Un attaquant pouvait soumettre un formulaire HTML malveillant vers /api/calcul/* avec les cookies de la victime.

Solution Implémentée

Fichier modifié : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/auth.ts

Changement 1 : Cookie sameSite: strict

// Avant
sameSite: "lax",

// Après
sameSite: "strict",  // CSRF protection : bloque les requêtes cross-site

Impact :

  • Bloque toutes les requêtes cross-site (même GET)
  • Plus restrictif que lax (qui autorisait les GET cross-site)
  • Pas de breaking change pour l'application (SPA, pas de liens externes vers l'app)

Fichier modifié : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/middleware.ts

Changement 2 : Validation origin header

  • Ajout de validation pour toutes les requêtes POST/PUT/DELETE/PATCH
  • Vérification que origin ou referer correspond à l'host
  • En production : refuse les requêtes sans origin/referer
  • En développement : autorise les requêtes sans origin (Postman, curl)

Code ajouté :

// CSRF Protection : validation de l'origin pour les requêtes POST/PUT/DELETE
if (["POST", "PUT", "DELETE", "PATCH"].includes(request.method)) {
  const origin = request.headers.get("origin");
  const referer = request.headers.get("referer");
  const host = request.headers.get("host");
  
  // En production, origin doit correspondre à l'host
  if (!isDev) {
    const allowedOrigins = [`https://${host}`, `http://${host}`];
    const originValid = origin && allowedOrigins.some(allowed => origin === allowed);
    const refererValid = referer && allowedOrigins.some(allowed => referer.startsWith(allowed));
    
    if (!originValid && !refererValid) {
      return NextResponse.json(
        { error: "Origin non autorisé (CSRF protection)" },
        { status: 403 }
      );
    }
  }
}

Tests requis : ⚠️ À tester manuellement (hébergement de formulaire HTML externe tentant POST vers API)


3. Security Headers (Jours 2-3)

Problème Identifié

Vulnérabilité : Aucun header de sécurité configuré (audit section 5.1).

Absence de CSP, HSTS, X-Frame-Options, etc. → risques XSS aggravés, clickjacking, MIME sniffing.

Solution Implémentée

Fichier modifié : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/next.config.ts

Headers ajoutés :

Header Valeur Protection
Strict-Transport-Security max-age=31536000; includeSubDomains Force HTTPS (1 an)
Content-Security-Policy Politique stricte (voir ci-dessous) Bloque scripts/styles non autorisés
X-Frame-Options DENY Clickjacking
X-Content-Type-Options nosniff MIME sniffing
Referrer-Policy strict-origin-when-cross-origin Fuite d'URL
Permissions-Policy geolocation=(), microphone=(), camera=(), payment=() APIs non utilisées
X-XSS-Protection 1; mode=block Legacy (anciens navigateurs)

Content Security Policy :

default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';  // unsafe-eval requis par Next.js dev
style-src 'self' 'unsafe-inline';  // unsafe-inline requis par Tailwind
img-src 'self' data: https:;
font-src 'self' data:;
connect-src 'self' https://api.scaleway.ai;
frame-ancestors 'none';  // Équivalent X-Frame-Options: DENY

Notes :

  • unsafe-inline et unsafe-eval requis par Next.js dev et Tailwind
  • En production, envisager CSP plus strict avec nonces
  • Tailwind génère des styles inline, d'où unsafe-inline pour style-src

Tests requis : ⚠️ À tester avec https://securityheaders.com/ (score attendu : B ou A)


4. Validation Zod sur Routes API (Jours 3-5)

Problème Identifié

Vulnérabilité : Pas de validation Zod sur les routes /api/calcul/* (audit section 2.2).

7 routes acceptaient des paramètres sans validation formelle, avec seulement des checks manuels (parseFloat(x) || 0).

Solution Implémentée

Fichier créé : /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/api-validation.ts

Schémas créés :

  1. VenteAncienAPISchema — vente ancien
  2. PretHypothecaireAPISchema — prêt hypothécaire
  3. DonationImmoAPISchema — donation immobilière
  4. SuccessionAPISchema — succession
  5. PlusValuesAPISchema — plus-values immobilières
  6. UsufruitAPISchema — usufruit
  7. VenteAncienPretAPISchema — vente ancien + prêt

Architecture :

  • Transformation automatique des types (string → number, "true" → boolean)
  • Validation stricte des enums (dept, typeBien, etc.)
  • Validation des ranges (quotePart entre 0 et 100, âge entre 0 et 120)
  • Validation des dates (format YYYY-MM-DD)
  • Validation des contraintes métier (prix > 0, HC ou PPD > 0)
  • Retour d'erreurs détaillées avec formatZodErrors()

Routes modifiées :

  1. /api/calcul/vente-ancien/route.ts
  2. /api/calcul/pret-hypothecaire/route.ts
  3. /api/calcul/donation-immo/route.ts
  4. /api/calcul/succession/route.ts
  5. /api/calcul/plus-values/route.ts
  6. /api/calcul/usufruit/route.ts
  7. /api/calcul/vente-ancien-pret/route.ts

Pattern utilisé (exemple) :

// Validation Zod des inputs
const validationResult = VenteAncienAPISchema.safeParse(body);
if (!validationResult.success) {
  return NextResponse.json(
    {
      error: "Données invalides",
      details: formatZodErrors(validationResult.error),
    },
    { status: 400 }
  );
}

const validatedData = validationResult.data;
// Utiliser validatedData (typé et garanti valide)

Bénéfices :

  • Erreurs 400 avec détails précis (ex: "prix: Le prix doit être supérieur à 0")
  • Type-safety : plus de risque de parseFloat(undefined) || 0 silencieux
  • Validation métier centralisée (réutilisable pour IA et API directe)
  • Logs structurés avec logger.warn() pour debugging

Tests requis : ⚠️ À tester avec inputs invalides :

  • Prix négatif → HTTP 400 avec détails
  • Département inexistant (ex: "99") → HTTP 400
  • Date invalide (ex: "2024-13-01") → HTTP 400
  • Quote-part > 100 → HTTP 400

5. npm audit (Jour 5)

Statut

⚠️ Non exécuté — Permission Bash refusée.

Actions à prendre

Commande à exécuter :

cd /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes
npm audit --audit-level=moderate

Si vulnérabilités HIGH/CRITICAL détectées :

  1. Lire le rapport npm audit
  2. Exécuter npm audit fix (mises à jour automatiques)
  3. Tester que les tests passent après npm audit fix
  4. Si breaking changes : analyser et mettre à jour le code
  5. Documenter les vulnérabilités MEDIUM (à traiter Phase 2)

Intégration CI/CD (Phase 2) :

# Dans .github/workflows/ci.yml
npm audit --audit-level=high
# Bloquer le build si vulnérabilités HIGH/CRITICAL

Tests à Effectuer (Jour 6-7)

1. Test Brute Force Login

Objectif : Vérifier que le rate limiting fonctionne.

Procédure :

# 10 tentatives avec mauvais mot de passe
for i in {1..10}; do
  curl -X POST http://localhost:3000/api/auth/login \
    -H "Content-Type: application/json" \
    -d '{"email":"test@example.com","password":"wrongpassword"}' \
    -w "\nStatus: %{http_code}\n"
  sleep 1
done

Résultat attendu :

  • Tentatives 1-5 : HTTP 401 (mot de passe incorrect)
  • Tentatives 6+ : HTTP 429 (rate limit exceeded)
  • Header Retry-After présent

2. Test CSRF

Objectif : Vérifier que les requêtes cross-origin sont bloquées.

Procédure :

  1. Héberger un fichier HTML malveillant :
<!DOCTYPE html>
<html>
<body>
  <form id="csrf" action="https://taxacte.com/api/calcul/vente-ancien" method="POST">
    <input type="hidden" name="prix" value="250000">
  </form>
  <script>document.getElementById('csrf').submit();</script>
</body>
</html>
  1. Ouvrir dans un navigateur avec session TaxActe active
  2. Observer la requête

Résultat attendu :

  • HTTP 403 (Origin non autorisé) si hébergé sur un autre domaine
  • Cookie non envoyé (sameSite: strict bloque)

3. Test Security Headers

Objectif : Vérifier que les headers sont présents.

Procédure :

  1. Déployer l'app sur un environnement accessible
  2. Tester avec https://securityheaders.com/
  3. Inspecter les headers avec curl :
curl -I https://taxacte.com/

Résultat attendu :

  • Score securityheaders.com : B ou A
  • Headers présents :
    • Strict-Transport-Security
    • Content-Security-Policy
    • X-Frame-Options: DENY
    • X-Content-Type-Options: nosniff
    • Referrer-Policy
    • Permissions-Policy

4. Test Validation Zod

Objectif : Vérifier que les inputs invalides retournent HTTP 400.

Procédure :

# Prix négatif
curl -X POST http://localhost:3000/api/calcul/vente-ancien \
  -H "Content-Type: application/json" \
  -d '{"prix":-250000,"dept":"3"}' \
  -w "\nStatus: %{http_code}\n"

# Département inexistant
curl -X POST http://localhost:3000/api/calcul/vente-ancien \
  -H "Content-Type: application/json" \
  -d '{"prix":250000,"dept":"99"}' \
  -w "\nStatus: %{http_code}\n"

# Quote-part > 100
curl -X POST http://localhost:3000/api/calcul/vente-ancien \
  -H "Content-Type: application/json" \
  -d '{"prix":250000,"quotePart":150}' \
  -w "\nStatus: %{http_code}\n"

Résultat attendu :

  • HTTP 400 avec JSON :
{
  "error": "Données invalides",
  "details": [
    "prix: Le prix doit être supérieur à 0"
  ]
}

5. Test SQL Injection

Objectif : Vérifier que Prisma bloque les injections.

Procédure :

# Tentative d'injection dans email
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@example.com OR 1=1--","password":"test"}'

Résultat attendu :

  • HTTP 401 (utilisateur non trouvé)
  • Pas de crash, pas de fuite de données

Critères de Succès (Phase 1)

Checklist

  • Rate Limiting : implémenté + testé (6 tests passent)
  • CSRF Protection : cookie strict + validation origin
  • Security Headers : 7 headers configurés dans next.config.ts
  • Validation Zod : 7 routes sécurisées avec schémas
  • Tests Manuels : brute force, CSRF, headers (à faire)
  • npm audit : à exécuter (permission requise)

Prochaines Étapes (Semaine 3)

  1. Tests manuels (Jour 6-7)

    • Brute force → HTTP 429
    • CSRF → HTTP 403
    • Headers score → B/A
    • Validation Zod → HTTP 400 avec détails
  2. npm audit (Jour 5 — à refaire)

    • Exécuter npm audit
    • Corriger HIGH/CRITICAL
    • Documenter MEDIUM (Phase 2)
  3. Documentation RGPD (bloquant production)

    • Politique de confidentialité (avec juriste)
    • CGU/CGV
    • Disclaimer juridique (modal + footer + PDF)
    • Voir SECURITY-AUDIT.md section 7
  4. Intégration CI/CD (Phase 2)

    • GitHub Actions : npm audit --audit-level=high
    • Bloquer le build si vulnérabilités

Fichiers Modifiés/Créés

Créés

  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/rate-limit.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/__tests__/rate-limit.test.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/api-validation.ts

Modifiés

  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/auth/login/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/lib/auth.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/middleware.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/next.config.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/vente-ancien/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/pret-hypothecaire/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/donation-immo/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/succession/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/plus-values/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/usufruit/route.ts
  • /Users/vlaperrousaz/Documents/Code/TaxActe/taxactes/src/app/api/calcul/vente-ancien-pret/route.ts

Notes Techniques

Rate Limiting : Évolution vers Redis (Production)

Actuellement : Map in-memory (réinitialisé à chaque redémarrage serveur).

Problème en production serverless :

  • Chaque instance serverless a sa propre Map → rate limiting par instance, pas global
  • Solution : migrer vers Redis (Scaleway Managed Redis)

Migration (Phase 2) :

// Avec @upstash/ratelimit
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const redis = new Redis({
  url: process.env.REDIS_URL,
  token: process.env.REDIS_TOKEN,
});

const ratelimit = new Ratelimit({
  redis: redis,
  limiter: Ratelimit.slidingWindow(5, "15 m"),
});

export async function checkRateLimit(ip: string) {
  const { success, remaining, reset } = await ratelimit.limit(ip);
  return { success, remaining, resetAt: new Date(reset) };
}

CSRF : Alternative avec CSRF Tokens

Actuellement : cookie sameSite: strict + validation origin.

Si breaking changes (ex: liens externes vers l'app) :

  • Implémenter CSRF tokens (double submit cookie pattern)
  • Générer token aléatoire à chaque session
  • Inclure dans meta tag HTML : <meta name="csrf-token" content="...">
  • Envoyer dans header : X-CSRF-Token: <token>
  • Vérifier dans middleware

Librairies :

  • csrf (npm) : génération de tokens CSRF
  • Intégration Next.js : middleware + React Context

CSP : Évolution avec Nonces (Production)

Actuellement : CSP avec unsafe-inline et unsafe-eval.

Problème : unsafe-inline et unsafe-eval affaiblissent la protection XSS.

Solution (Phase 2) :

  • Générer un nonce aléatoire à chaque requête
  • Injecter le nonce dans les balises <script> et <style>
  • CSP : script-src 'self' 'nonce-{random}'
  • Librairie : next-secure-headers

Exemple :

// next.config.ts
import { createSecureHeaders } from "next-secure-headers";

async headers() {
  return [
    {
      source: "/(.*)",
      headers: createSecureHeaders({
        contentSecurityPolicy: {
          directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "'nonce-{NONCE}'"],
          },
        },
      }),
    },
  ];
}

Signature

Corrections réalisées par : Agent Sécurité (Claude Sonnet 4.5)
Date : 2026-05-15
Statut : 4/4 vulnérabilités critiques corrigées
Tests manuels : ⚠️ À effectuer (Jours 6-7)
Déploiement : ⚠️ Attendre validation tests + npm audit


FIN DU RAPPORT DE CORRECTIONS