# Revue securite apres corrections Date: 2026-05-06 Base de comparaison: `SECURITY_AUDIT_BASE.md`, `SECURITY_FIX_PLAN_FOR_CLAUDE.md`, `SECURITY_FIXES_APPLIED.md` et code actuel. Mode: revue statique locale, sans exploitation. Aucun fichier applicatif n'a ete modifie. ## Synthese Les corrections declarees dans `SECURITY_FIXES_APPLIED.md` sont presentes dans le code pour les trois premiers sujets: changement force au premier login, rate limiting du login, validation minimale du changement de compte. Elles reduisent le risque principal de bootstrap et de bruteforce. La correction SEC-FIX-001 reste partielle pour les installations deja creees avant la migration: l'ajout de colonne met `must_change_password` a `0` par defaut. Dans la base locale observee, l'utilisateur `admin` a `must_change_password=0`. Si ce compte utilise encore l'ancien mot de passe public, il ne sera pas force a changer. La documentation est egalement en retard sur le code: plusieurs fichiers decrivent encore `get_current_user` comme dependance des routeurs metier et ne documentent pas le nouveau flux `must_change_password`. Verification effectuee: - Parsing Python par AST: OK, 9 fichiers parses. - Build frontend Vite vers `/tmp/topologie-vite-build`: OK. - Aucun test automatise dedie n'a ete trouve dans `backend` ou `frontend`. ## SEC-FIX-001 - Changement de mot de passe obligatoire au premier login Statut: partiellement corrige Preuve dans le code: - `backend/models.py:6-12` ajoute `User.must_change_password`. - `backend/main.py:78-110` cree `admin` avec `must_change_password=1` si `INITIAL_ADMIN_PASSWORD` est absent, et `0` si la variable est fournie. - `backend/main.py:130-133` protege les routeurs metier avec `Depends(require_password_changed)`. - `backend/routers/auth.py:97-100` refuse les routeurs proteges avec 403 si le changement est requis. - `backend/routers/auth.py:149-172` remet `must_change_password` a `False` apres changement de mot de passe. - `frontend/src/App.vue:3-11` affiche `AccountModal` forcee avant l'application si `mustChangePassword` est vrai. - `frontend/src/components/AccountModal.vue:93-97` exige un nouveau mot de passe cote client en mode force. Risque restant: - `backend/main.py:63-75` ajoute la colonne aux bases existantes avec `DEFAULT 0`; les comptes deja presents ne sont donc pas forces a changer. - La base locale contient `admin|0` pour `must_change_password`. - `docker-compose.yml` ne montre pas `INITIAL_ADMIN_PASSWORD`; `.env.example` est absent. - `AGENTS.md`, `CLAUDE.md` et `docs/backend.md` decrivent encore l'ancien modele `Depends(get_current_user)` ou l'ancien contrat de login. Recommandation finale: - Ajouter une migration de rattrapage explicite pour les anciennes bases, ou documenter une commande de remediation obligatoire. - Documenter `INITIAL_ADMIN_PASSWORD`, `must_change_password` et `require_password_changed` dans `AGENTS.md`, `CLAUDE.md`, `docs/*` et `.env.example`. - Ajouter un test couvrant base vide, base existante et acces CRUD avant/apres changement. ## SEC-FIX-002 - Rate limiting sur POST /api/auth/login Statut: corrige Preuve dans le code: - `frontend/rate_limit.conf:1` definit `limit_req_zone`. - `frontend/Dockerfile:10-11` copie `nginx.conf` et `rate_limit.conf`. - `frontend/nginx.conf:6-13` applique `limit_req zone=login burst=5 nodelay` et retourne 429 sur `/api/auth/login`. - `backend/routers/auth.py:43-76` implemente un rate limit in-memory par username et par IP. - `backend/routers/auth.py:132-146` applique ces controles avant verification du mot de passe. - `frontend/src/components/LoginPage.vue:69-74` affiche un message dedie sur 429. Risque restant: - Le rate limit backend est en memoire: il est perdu au redemarrage et se fragmente avec plusieurs workers/process. - `Request.client.host` voit souvent l'adresse du proxy Nginx, pas le client final; le controle IP backend peut donc devenir global derriere proxy. Le controle Nginx compense dans le deploiement Compose fourni. - Les compteurs IP backend ne sont pas remis a zero apres login reussi. - Aucun test automatise de fenetre, expiration ou non-enumeration n'a ete trouve. Recommandation finale: - Conserver le controle Nginx et soit documenter que le backend ne doit pas etre expose directement, soit gerer proprement `X-Forwarded-For` depuis un proxy de confiance. - Ajouter des tests API pour echecs successifs, expiration de fenetre et succes apres expiration. ## SEC-FIX-003 - Validation serveur des entrees sur PUT /api/auth/account Statut: partiellement corrige Preuve dans le code: - `backend/routers/auth.py:103-117` valide username et mot de passe. - `backend/routers/auth.py:157-165` applique ces validations avant modification. - `frontend/src/components/AccountModal.vue:91-117` applique une validation miroir. - `frontend/src/i18n.js:160-165`, `310-315`, `460-465` contient les messages localises. Risque restant: - La politique mot de passe reste minimale: `password1` ou `admin123` respectent la regle lettre+chiffre et longueur 8. - `AccountUpdate` accepte les champs optionnels sans contraintes Pydantic; la robustesse depend des validations manuelles. - Les erreurs `Username already taken` et `Current password is incorrect` ne sont pas sous forme de codes stables et restent moins homogenes que les nouveaux codes. - Aucun test API dedie n'a ete trouve. Recommandation finale: - Renforcer la politique avec une liste de mots de passe evidents/refuses et des contraintes Pydantic (`Field`, `StringConstraints`) en plus des helpers. - Normaliser/trim le username avant comparaison et stockage. - Ajouter des tests pour mot de passe faible evident, username invalide, doublon et cas valide. ## SEC-FIX-004 - JWT localStorage, expiration et invalidation Statut: non corrige Preuve dans le code: - `frontend/src/auth.js:3-17` stocke toujours le JWT dans `localStorage`. - `frontend/src/api.js:6-9` continue d'envoyer un bearer token lu par JavaScript. - `backend/routers/auth.py:38-80` conserve une expiration de 7 jours et un payload `{sub, exp}` sans version de session. - `backend/routers/auth.py:162-168` change le mot de passe sans invalider les tokens deja emis. Risque restant: - Vol/rejeu de JWT en cas de XSS, extension compromise ou poste compromis. - Ancien token toujours valide apres changement de mot de passe jusqu'a expiration. Recommandation finale: - Migrer vers cookie `HttpOnly`, `Secure`, `SameSite` ou, a minima, ajouter `session_version`/`token_version` en base et reduire l'expiration. ## SEC-FIX-005 - CORS configurable ou supprime Statut: non corrige Preuve dans le code: - `backend/main.py:122-127` conserve `allow_origins=["*"]`, `allow_methods=["*"]`, `allow_headers=["*"]`. - `.env.example` est absent. Risque restant: - Configuration permissive si le backend est publie directement ou si l'auth migre vers cookies. Recommandation finale: - Supprimer CORS en mode meme origine ou ajouter `ALLOWED_ORIGINS` explicite. - Documenter que le backend doit rester interne. ## SEC-FIX-006 - Encadrement discovery scan/ping Statut: non corrige Preuve dans le code: - `backend/routers/discovery.py:72-86` accepte `ips: list[str]` sans validation IP ni limite de taille. - `backend/routers/discovery.py:89-129` valide seulement le format CIDR et limite chaque reseau a 1024 hotes, mais pas le total de targets ni le perimetre autorise. - `backend/routers/discovery.py:52-60` accepte un serveur DNS fourni librement. Risque restant: - Reconnaissance reseau authentifiee, surcharge CPU/process et DNS arbitraire. Recommandation finale: - Valider IP/CIDR/DNS, plafonner le total par requete, autoriser seulement les CIDR d'inventaire ou une allowlist, et ajouter rate limit. ## SEC-FIX-007 - Validation stricte des modeles metier Statut: non corrige Preuve dans le code: - `backend/routers/devices.py:11-33`, `backend/routers/vlans.py:11-15`, `backend/routers/links.py:11-15` utilisent encore des `str` libres sans bornes, enums ni validation URL/IP/CIDR/couleur. - Les liens externes ont `rel="noopener"` mais pas `noreferrer`: `frontend/src/components/DeviceManager.vue:137`, `frontend/src/components/TopologyGraph.vue:35`, `56`, `104`, `142`. Risque restant: - Donnees incoherentes, champs enormes, URL dangereuses et surface XSS future. Recommandation finale: - Ajouter contraintes Pydantic v2, enums serveur, validation URL `http/https`, IP/CIDR et couleur hex. - Remplacer `rel="noopener"` par `rel="noreferrer noopener"`. ## SEC-FIX-008 - En-tetes de securite HTTP Statut: non corrige Preuve dans le code: - `frontend/nginx.conf:1-25` ne definit pas CSP, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, `X-Frame-Options` ou `frame-ancestors`. - La seule configuration ajoutee concerne `limit_req`: `frontend/nginx.conf:6-8`. Risque restant: - Durcissement navigateur absent dans la configuration fournie. Recommandation finale: - Ajouter les en-tetes dans Nginx ou fournir une configuration reverse-proxy de reference verifiee. ## SEC-FIX-009 - Contrat TLS/reverse-proxy Statut: non corrige Preuve dans le code: - `docker-compose.yml:14-15` expose toujours `"8080:80"` sur toutes les interfaces. - Aucune `.env.example` ni documentation de bind local/reverse proxy TLS n'a ete ajoutee. Risque restant: - Exposition HTTP involontaire sur un LAN non fiable avec interception d'identifiants/tokens. Recommandation finale: - Documenter HTTPS obligatoire hors local et recommander `127.0.0.1:8080:80` pour un proxy local. - Fournir un exemple reverse-proxy TLS. ## SEC-FIX-010 - Gestion du secret JWT Statut: non corrige Preuve dans le code: - `backend/routers/auth.py:20-33` cree toujours `data/secret_key.txt` via `open(..., "w")` sans permissions explicites. - `docker-compose.yml:6-7` monte toujours `./db_data:/app/data`. - `.gitignore` et `.env.example` sont absents. Risque restant: - Fuite locale de cle JWT et forge de tokens jusqu'a rotation. Recommandation finale: - Creer le fichier avec permissions `0600`, ignorer `db_data/`, documenter rotation et supporter un secret Docker/env obligatoire en production. ## SEC-FIX-011 - Durcissement conteneurs Statut: non corrige Preuve dans le code: - `backend/Dockerfile:1-8` et `frontend/Dockerfile:8-12` ne definissent pas d'utilisateur non-root. - `docker-compose.yml:1-20` ne definit pas `cap_drop`, `security_opt`, `read_only`, limites ressources ni healthchecks. Risque restant: - Impact augmente en cas de compromission d'un conteneur. Recommandation finale: - Passer en non-root, ajouter `cap_drop: [ALL]`, garder `NET_RAW` seulement pour le backend, ajouter `no-new-privileges`, healthchecks et volumes temporaires explicites. ## SEC-FIX-012 - Logs securite structures Statut: non corrige Preuve dans le code: - Aucun logging structure n'est present dans `backend/routers/auth.py`, `devices.py`, `vlans.py`, `links.py` ou `discovery.py`. Risque restant: - Investigation limitee en cas d'incident: login, scans, suppressions et rate limits ne sont pas tracables. Recommandation finale: - Ajouter logs structures sans secrets pour authentification, changements de compte, CRUD, scans et rate limits. ## SEC-FIX-013 - Foreign keys SQLite Statut: non corrige Preuve dans le code: - `backend/database.py:7-10` cree l'engine sans event SQLAlchemy pour `PRAGMA foreign_keys=ON`. - Les `ForeignKey` restent declares dans `backend/models.py`, mais SQLite ne les enforce pas sans pragma par connexion. Risque restant: - Incoherences relationnelles possibles via evolutions futures, migrations ou erreurs applicatives. Recommandation finale: - Activer `PRAGMA foreign_keys=ON` via event SQLAlchemy et tester suppressions/creation d'interfaces/liens invalides. ## SEC-FIX-014 - Supply chain et builds reproductibles Statut: non corrige Preuve dans le code: - `frontend/Dockerfile:3-4` utilise toujours `npm install` au lieu de `npm ci`. - `frontend/package.json` contient encore `cytoscape`. - Aucune procedure d'audit dependances n'a ete ajoutee. Risque restant: - Builds moins reproductibles et dependances inutiles/vulnerables plus difficiles a suivre. Recommandation finale: - Utiliser `npm ci`, supprimer `cytoscape` s'il est inutilise, documenter `npm audit` et l'audit Python en conservant le pin `bcrypt==3.2.2`. ## SEC-FIX-015 - Import JSON borne et valide Statut: non corrige Preuve dans le code: - `frontend/src/App.vue:198-220` lit le fichier complet, parse sans schema, importe en boucle et ignore silencieusement les erreurs par `.catch(() => {})`. Risque restant: - Import incoherent, erreurs masquees et deni de service local par fichier volumineux. Recommandation finale: - Ajouter limite de taille, validation de schema, compteur d'erreurs et retour utilisateur detaille. ## SEC-FIX-016 - Suppression de v-html Statut: non corrige Preuve dans le code: - `frontend/src/App.vue:30` utilise encore `v-html="tab.icon"`. Risque restant: - Risque XSS defense-in-depth si une future icone devient dynamique ou non controlee. Recommandation finale: - Remplacer par texte, composants d'icones ou SVG statique sans `v-html`. ## SEC-FIX-017 - Endpoint health public documente ou restreint Statut: partiellement corrige Preuve dans le code: - `backend/main.py:136-138` expose toujours `/api/health` publiquement avec seulement `{"status": "ok"}`, sans detail sensible. - La decision de securite n'est pas documentee dans les fichiers lus. Risque restant: - Exposition inutile d'information de disponibilite si le proxy publie trop largement l'API. Recommandation finale: - Documenter explicitement que l'endpoint est public et minimal, ou le restreindre au proxy/healthcheck. ## SEC-FIX-018 - Documentation securite et environnement Statut: non corrige Preuve dans le code: - `README.md` et `.env.example` sont absents. - `AGENTS.md:66`, `CLAUDE.md:171` et `docs/backend.md:5-12` decrivent encore les routeurs metier avec `get_current_user` au lieu de `require_password_changed`. - `docs/backend.md:28-36` ne reflete pas le nouveau champ `must_change_password` ni les erreurs de validation. Risque restant: - Operateur guide par une documentation obsolete, avec configuration dev/prod et secrets insuffisamment explicites. Recommandation finale: - Ajouter `README.md` et `.env.example`, puis mettre a jour `AGENTS.md`, `CLAUDE.md` et `docs/*` pour `INITIAL_ADMIN_PASSWORD`, `SECRET_KEY`, CORS, TLS, rotation de cle, sauvegarde/restauration et profils dev/prod. ## Conclusion Les trois corrections declarees sont globalement integrees, mais seules SEC-FIX-002 peut etre consideree corrigee avec des risques residuels acceptables pour le mode Compose actuel. SEC-FIX-001 et SEC-FIX-003 restent partielles. Les autres taches du plan ne sont pas encore appliquees. Priorite recommandee: 1. Corriger la migration/strategie de rattrapage pour les comptes existants `admin`. 2. Mettre a jour la documentation et ajouter `.env.example`. 3. Ajouter des tests API pour auth, rate limit et changement force. 4. Continuer les corrections P1 restantes: JWT/session, discovery, validation metier et secret JWT.