186 lines
7.3 KiB
Markdown
186 lines
7.3 KiB
Markdown
|
|
# PHASE F — SÉCURITÉ FRONTEND
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## F1. Secrets et données sensibles
|
||
|
|
|
||
|
|
### Variables VITE_* exposées au bundle
|
||
|
|
|
||
|
|
| Variable | Sensible ? | Verdict |
|
||
|
|
|----------|-----------|---------|
|
||
|
|
| `VITE_API_URL` | ❌ Non — URL publique | ✅ OK |
|
||
|
|
| `VITE_WS_URL` | ❌ Non — URL publique | ✅ OK |
|
||
|
|
| `VITE_STREAM_URL` | ❌ Non — URL publique | ✅ OK |
|
||
|
|
| `VITE_UPLOAD_URL` | ❌ Non — URL publique | ✅ OK |
|
||
|
|
| `VITE_APP_NAME` | ❌ Non — branding | ✅ OK |
|
||
|
|
| `VITE_DEBUG` | ⚠️ Potentiel — active le debug | ⚠️ S'assurer false en prod |
|
||
|
|
| `VITE_USE_MSW` | ⚠️ Potentiel — active le mocking | ⚠️ S'assurer false en prod |
|
||
|
|
| `VITE_FCM_VAPID_KEY` | ⚠️ Limité — clé publique Firebase | ✅ OK (clé publique par design) |
|
||
|
|
| `VITE_FEATURE_*` | ❌ Non — feature flags | ✅ OK |
|
||
|
|
| `VITE_SENTRY_DSN` | ❌ Non — DSN public par design | ✅ OK |
|
||
|
|
| `VITE_DOMAIN` | ❌ Non — domaine public | ✅ OK |
|
||
|
|
|
||
|
|
### Secrets dans le code source
|
||
|
|
|
||
|
|
- ✅ **Aucun secret hardcodé** détecté dans le code source
|
||
|
|
- ✅ Tokens de test uniquement dans les fichiers mock (`mocks/handlers.ts`)
|
||
|
|
- ✅ Pas de clés API, mots de passe, ou secrets privés
|
||
|
|
|
||
|
|
### Fichiers .env
|
||
|
|
|
||
|
|
| Fichier | Tracké | Verdict |
|
||
|
|
|---------|--------|---------|
|
||
|
|
| `.env.example` | ✅ Tracké (template) | ✅ OK |
|
||
|
|
| `.env.local` | ❌ Non tracké | ✅ OK |
|
||
|
|
| `.env.storybook` | ✅ Tracké | ⚠️ Vérifier qu'il ne contient pas de secrets |
|
||
|
|
| `.env.production` | ✅ Tracké | ⚠️ Vérifier qu'il ne contient pas de secrets réels |
|
||
|
|
|
||
|
|
### Stockage JWT
|
||
|
|
|
||
|
|
- ✅ **httpOnly cookies** — tokens JWT stockés dans des cookies httpOnly par le backend [services/tokenStorage.ts]
|
||
|
|
- ✅ JavaScript ne peut pas lire les tokens (getters retournent `null`)
|
||
|
|
- ✅ Nettoyage des vestiges localStorage inclus
|
||
|
|
- ✅ **Excellente pratique de sécurité**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## F2. XSS
|
||
|
|
|
||
|
|
### dangerouslySetInnerHTML
|
||
|
|
|
||
|
|
| Fichier | Ligne | Source des données | Sanitization | Verdict |
|
||
|
|
|---------|-------|-------------------|-------------|---------|
|
||
|
|
| `features/chat/components/ChatMessages.tsx` | 145 | `message.content` (user input) | ✅ `sanitizeChatMessage()` | ✅ Sûr |
|
||
|
|
| `features/chat/components/virtualized-chat-messages/VirtualizedChatMessageItem.tsx` | 58 | `message.content` (user input) | ✅ `sanitizeChatMessage()` | ✅ Sûr |
|
||
|
|
|
||
|
|
### Implémentation de la sanitization
|
||
|
|
|
||
|
|
**Fichier** : `utils/sanitize.ts` (429 lignes)
|
||
|
|
|
||
|
|
**Configuration DOMPurify** :
|
||
|
|
- Tags autorisés : `p, br, strong, em, u, i, b, span, a`
|
||
|
|
- Attributs autorisés : `class, href, title, target`
|
||
|
|
- URI schemes autorisés : `http, https, mailto` uniquement
|
||
|
|
- Tags interdits : `script, iframe, object, embed, form, input, button`
|
||
|
|
- Attributs interdits : `onerror, onload, onclick, onmouseover, onfocus, onblur`
|
||
|
|
- Fallback de sanitization manuelle si DOMPurify indisponible
|
||
|
|
|
||
|
|
**Verdict** : ✅ **Robuste**. La sanitization est correctement implémentée avec DOMPurify et une configuration restrictive.
|
||
|
|
|
||
|
|
### Autres vecteurs XSS
|
||
|
|
|
||
|
|
- ✅ **Aucun `eval()`** ou `new Function()` détecté
|
||
|
|
- ✅ **Aucun `.innerHTML =`** direct
|
||
|
|
- ✅ **Aucun `document.write`**
|
||
|
|
- ✅ React échappe automatiquement le rendu JSX par défaut
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## F3. Stockage client
|
||
|
|
|
||
|
|
### localStorage
|
||
|
|
|
||
|
|
| Donnée | Fichier | Sensible ? | Verdict |
|
||
|
|
|--------|---------|-----------|---------|
|
||
|
|
| Density preference | `features/tracks/components/TrackGrid.tsx:60,84` | ❌ Non | ✅ OK |
|
||
|
|
| (Legacy token cleanup) | `services/tokenStorage.ts` | — | ✅ Nettoyé |
|
||
|
|
|
||
|
|
### sessionStorage
|
||
|
|
|
||
|
|
| Donnée | Fichier | Sensible ? | Verdict |
|
||
|
|
|--------|---------|-----------|---------|
|
||
|
|
| Deprecation warnings | `services/api/client.ts:773-774,872-884` | ❌ Non | ✅ OK |
|
||
|
|
| API error tracking | `services/api/client.ts:1347` | ❌ Non | ✅ OK |
|
||
|
|
|
||
|
|
**Verdict** : ✅ Aucune donnée sensible en localStorage/sessionStorage. Les tokens sont dans des cookies httpOnly.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## F4. Dépendances
|
||
|
|
|
||
|
|
### Build cassé
|
||
|
|
|
||
|
|
- 🔴 **Le build échoue** : `educationService` manquant (import fantôme dans `useEducationView.ts`)
|
||
|
|
- Cela empêche l'exécution de `npm audit` sur un build propre
|
||
|
|
|
||
|
|
### Dépendances notables
|
||
|
|
|
||
|
|
| Dépendance | Version | Risque |
|
||
|
|
|------------|---------|--------|
|
||
|
|
| `dompurify` | ^3.3.0 | ✅ Dernière version — sanitization XSS |
|
||
|
|
| `axios` | ^1.13.5 | ✅ Récent |
|
||
|
|
| `react` | ^18.2.0 | ✅ Stable |
|
||
|
|
| `zod` | ^3.25.76 | ✅ Validation schema |
|
||
|
|
| `swagger-ui-react` | ^5.31.0 | ⚠️ En prod deps — potentiel d'attaque via UI Swagger exposée |
|
||
|
|
| `lucide-react` | ^0.321.0 | ⚠️ Version ancienne (2024) — mise à jour recommandée |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## F5. Autres vecteurs
|
||
|
|
|
||
|
|
### CORS
|
||
|
|
|
||
|
|
- ✅ Proxy configuré en dev (`vite.config.ts`) — pas de CORS issues en dev
|
||
|
|
- ✅ `withCredentials: true` dans le client Axios pour les cookies
|
||
|
|
- [DONNÉES INSUFFISANTES — CORS headers côté serveur non analysés]
|
||
|
|
|
||
|
|
### CSP
|
||
|
|
|
||
|
|
- ✅ **Utilitaire CSP présent** (`utils/csp.ts`)
|
||
|
|
- ✅ Production CSP utilise des nonces (pas de `unsafe-inline` pour scripts)
|
||
|
|
- ✅ Dev CSP autorise `unsafe-eval` pour Vite HMR uniquement
|
||
|
|
- ⚠️ Les headers CSP doivent être injectés côté serveur — vérifier l'intégration
|
||
|
|
|
||
|
|
### CSRF
|
||
|
|
|
||
|
|
- ✅ **Implémenté** (`services/csrf.ts`)
|
||
|
|
- ✅ Token CSRF récupéré depuis `/csrf-token` endpoint
|
||
|
|
- ✅ Stocké en mémoire (pas en localStorage)
|
||
|
|
- ✅ Header `X-CSRF-Token` injecté via intercepteur Axios
|
||
|
|
- ✅ Mécanisme de refresh avec déduplification
|
||
|
|
|
||
|
|
### Open redirects
|
||
|
|
|
||
|
|
- ✅ **Aucune assignation `window.location =` détectée** avec input utilisateur
|
||
|
|
- ✅ Navigation via React Router uniquement
|
||
|
|
|
||
|
|
### Prototype pollution
|
||
|
|
|
||
|
|
- ✅ Pas d'usage de `lodash.merge` ou patterns similaires dangereux détectés
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Classement des vulnérabilités
|
||
|
|
|
||
|
|
| Gravité | Vulnérabilité | Localisation | Exploitabilité | Urgence |
|
||
|
|
|---------|--------------|--------------|----------------|---------|
|
||
|
|
| 🟡 MOYENNE | Build cassé — import fantôme bloque le build | `useEducationView.ts` → `educationService` | Non exploitable (build-time) | 🔴 Immédiate — bloque le déploiement |
|
||
|
|
| 🟡 MOYENNE | `VITE_DEBUG` / `VITE_USE_MSW` en production | Config env | Faible — info disclosure | ⚠️ Vérifier les valeurs en prod |
|
||
|
|
| 🟡 MOYENNE | Swagger UI en production deps | `package.json` | Faible — surface d'attaque via Swagger UI | ⚠️ Déplacer en devDependencies ou lazy-load |
|
||
|
|
| 🟢 BASSE | `.env.production` tracké | `.env.production` | Très faible — si ne contient que des valeurs publiques | 🟢 Vérifier le contenu |
|
||
|
|
| 🟢 BASSE | CSP headers non vérifiés côté serveur | Server config | Dépend du serveur | 🟢 Audit serveur nécessaire |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## SCORE SÉCURITÉ : 8/10
|
||
|
|
|
||
|
|
### Points gagnés
|
||
|
|
|
||
|
|
| Point | Score | Justification |
|
||
|
|
|-------|-------|---------------|
|
||
|
|
| JWT httpOnly cookies | +2.0 | Excellente pratique — tokens inaccessibles au JS |
|
||
|
|
| XSS sanitization | +1.5 | DOMPurify strict, pas d'eval, pas d'innerHTML |
|
||
|
|
| CSRF protection | +1.5 | Token CSRF en mémoire, header injection |
|
||
|
|
| Pas de secrets hardcodés | +1.0 | Aucun secret dans le code source |
|
||
|
|
| CSP utilities | +0.5 | Utilitaire prêt avec nonces |
|
||
|
|
| No open redirects | +0.5 | Navigation React Router uniquement |
|
||
|
|
| No prototype pollution | +0.5 | Pas de patterns dangereux |
|
||
|
|
|
||
|
|
### Points perdus
|
||
|
|
|
||
|
|
| Point | Score | Justification |
|
||
|
|
|-------|-------|---------------|
|
||
|
|
| Build cassé | -0.5 | Import fantôme bloque le déploiement |
|
||
|
|
| Swagger UI en prod deps | -0.3 | Surface d'attaque potentielle |
|
||
|
|
| Debug flags en env | -0.2 | VITE_DEBUG pourrait être activé en prod |
|