88cf6458d0
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>
109 lines
6.2 KiB
Markdown
109 lines
6.2 KiB
Markdown
# 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_id` contre 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` |
|