Files
stupid-simple-network-inven…/SECURITY_REVIEW_AFTER_FIXES.md
T
olivier 88cf6458d0 Initial commit — Stupid Simple Network Inventory
Application web d'inventaire réseau manuel avec FastAPI, Vue 3 et Docker.
Inclut l'authentification JWT, la découverte ICMP, et la topologie en cards CSS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 09:19:19 +02:00

15 KiB

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.