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>
6.2 KiB
Security Decisions — Phase 3
Date: 2026-05-06
Ce document enregistre les arbitrages pris lors de la phase 3, les alternatives écartées et les limitations acceptées.
SEC-FIX-006 — Cap global discovery
Décision : MAX_HOSTS_TOTAL = 4096 (4 × /22).
Alternatives écartées :
- Cap par nombre de targets : ne bloque pas 1 seul /11 découpé en CIDRs /22.
- Cap plus bas (1024 global) : trop restrictif pour les déploiements multi-VLAN légitimes.
Limitation : le cap s'applique à la requête, pas à l'utilisateur ou à la session. Un utilisateur authentifié peut lancer N requêtes consécutives. Mitigation acceptable : l'endpoint est protégé par authentification et nécessite CAP_NET_RAW.
SEC-FIX-007 — Validation Pydantic : périmètre choisi
Décision : validateurs sur les champs à domaine fini (type, virt_type, couleur, CIDR, IP, URL). Pas de validation sur description au-delà d'une longueur max.
Alternatives écartées :
- Liste noire de caractères dans
name/description: SQLAlchemy paramétrise toutes les requêtes → pas de risque SQL injection. La liste noire ajouterait une friction sans gain de sécurité. - Validation de
vlan_idcontre les VLANs existants dans l'interface (unicité déjà enforced en base).
Limite acceptée : name peut contenir des caractères Unicode arbitraires — ce n'est pas un vecteur d'injection dans ce contexte (pas de rendu HTML côté serveur, pas de shell).
SEC-FIX-008 — Content-Security-Policy
Décision : style-src 'self' 'unsafe-inline' conservé.
Pourquoi : Vue 3 utilise des attributs style="" inline (ex: style="display:none" pour v-show, styles dynamiques sur les chips de couleur VLAN). Ces attributs sont contrôlés par style-src. Supprimer 'unsafe-inline' casserait l'app sans migration vers CSS classes ou nonces.
Alternative écartée : nonces CSP pour les styles inline — Vite ne génère pas de nonces en production, nécessiterait un middleware serveur dynamique incompatible avec Nginx statique.
Décision : frame-ancestors 'none' + X-Frame-Options: DENY — redondant mais nécessaire pour les navigateurs qui n'implémentent pas CSP frame-ancestors.
SEC-FIX-012 — Logs d'audit : format et activation
Décision : JSON one-line via logging.getLogger("audit"), stdout.
Pourquoi : stdlib uniquement, pas de dépendance (structlog, loguru écartes). Docker collecte stdout. Le format JSON permet le parsing par Loki/ELK sans transformation.
Activation : uvicorn hérite de la config logging Python. Pour filtrer uniquement les événements d'audit : --log-config ou configuration Python logging dans main.py (non ajouté : hors périmètre de cette phase).
Limitation : les logs ne sont pas persistants entre redémarrages sans volume dédié ou système de log externe. Acceptable pour un déploiement self-hosted.
SEC-FIX-013 — PRAGMA foreign_keys=ON
Décision : listener sur l'événement connect de SQLAlchemy — s'applique à toutes les connexions, y compris les connexions de test.
Attention : la migration _migrate_vlan_nullable() dans main.py désactive temporairement PRAGMA foreign_keys=OFF pour recréer la table vlans. Elle le réactive ensuite. Ce pattern est correct car le listener ne s'applique qu'à la connexion DBAPI sous-jacente, pas aux connexions SQLAlchemy qui wrappent.
Conséquence sur SEC-FIX-017 : avec foreign_keys=ON, la table links (qui référençait devices) aurait bloqué les DELETE sur les équipements. C'est la raison pour laquelle _migrate_drop_links_table() est exécutée au démarrage.
SEC-FIX-015 — Limite import JSON côté frontend uniquement
Décision : limite de taille appliquée uniquement côté frontend (JavaScript, avant file.text()).
Limitation connue : cette protection est bypassable par un appel direct à l'API. Cependant :
- L'endpoint d'import JSON (
/api/devices/,/api/vlans/) est protégé par authentification. - FastAPI/uvicorn a une limite de corps par défaut (1 Mo via Starlette). Pour les requêtes individuelles, la validation Pydantic rejette les données invalides.
- La protection frontend couvre 99% des cas d'usage (import via UI).
Alternative écartée : limite de taille côté Nginx (client_max_body_size) — le scan de découverte et l'import sont deux flux différents ; limiter globalement pourrait bloquer des scans légitimes. Une limite fine par endpoint nécessiterait une configuration Nginx complexe.
SEC-FIX-016 — v-html
Décision : les icônes de navigation (■ ◆ ▣) sont des caractères Unicode hardcodés dans le computed. Pas de source externe, pas de traduction, pas d'interpolation.
Risque résiduel : nul — les caractères Unicode passent par {{ }} qui échappe automatiquement.
SEC-FIX-017 — Table links : DROP vs conservation
Décision : DROP de la table links via _migrate_drop_links_table() au démarrage.
Pourquoi : avec PRAGMA foreign_keys=ON (SEC-FIX-013), la table links (FK → devices.id sans ON DELETE CASCADE) aurait bloqué toute suppression d'équipement ayant des liens. Conserver la table sans le code applicatif pour la maintenir est une dette technique et un risque opérationnel.
Données perdues : les liens existants en base sont supprimés de façon irréversible. Acceptable car la fonctionnalité est retirée de l'interface — les données ne sont plus accessibles ni maintenables.
Alternative écartée : ajouter ON DELETE CASCADE sur la table existante. Aurait préservé les données mais complexifié la migration SQLite (pas d'ALTER CONSTRAINT) et laissé une table orpheline indéfiniment.
Points restants non traités en Phase 3
| ID | Raison du report |
|---|---|
| SEC-FIX-009 | Couvert : documentation TLS/HTTPS dans README (phases 2/3) |
| SEC-FIX-014 | Couvert : cytoscape supprimé en phase 2/3 |
| Audit log configuration fine | Hors périmètre — nécessite une décision d'infrastructure (Loki, ELK, etc.) |
no-new-privileges backend |
Contrainte technique inhérente à cap_net_raw=ep pour ping (documenté) |
| Rate limiting multi-worker | Mitigation en place via Nginx limit_req |