Files
FamilyPlanner/docs/archive/SECURITY_IMPROVEMENTS.md
philippe fdd72c1135 Initial commit: Family Planner application
Complete family planning application with:
- React frontend with TypeScript
- Node.js/Express backend with TypeScript
- Python ingestion service for document processing
- Planning ingestion service with LLM integration
- Shared UI components and type definitions
- OAuth integration for calendar synchronization
- Comprehensive documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 10:43:33 +02:00

252 lines
7.2 KiB
Markdown

# Améliorations de Sécurité - Family Planner
## 📋 Résumé des corrections appliquées
Ce document liste toutes les corrections de sécurité et d'hygiène de code appliquées le 2025-10-12.
---
## 🔐 1. Sécurisation de l'Upload Planning
### Problème identifié
- Aucune validation de type de fichier
- Pas de limite de taille
- Risque de path traversal via `childId`
### Corrections appliquées
**Fichier**: `backend/src/routes/uploads.ts`
#### A. FileFilter avec whitelist stricte
```typescript
const planFileFilter = (_req: Request, file: Express.Multer.File, callback: FileFilterCallback) => {
const allowedMimeTypes = [
'image/jpeg', 'image/jpg', 'image/png', 'image/webp',
'application/pdf',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
const allowedExtensions = /\.(jpg|jpeg|png|webp|pdf|xls|xlsx)$/i;
const hasValidMime = allowedMimeTypes.includes(file.mimetype);
const hasValidExt = allowedExtensions.test(file.originalname);
if (hasValidMime && hasValidExt) {
callback(null, true);
} else {
callback(new Error('Type de fichier non autorisé'));
}
};
```
#### B. Limite de taille de fichier
```typescript
const planUpload = multer({
storage: planStorage,
fileFilter: planFileFilter,
limits: {
fileSize: 10 * 1024 * 1024 // 10MB max
}
});
```
#### C. Validation UUID pour prévenir path traversal
```typescript
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!rawChildId || !uuidRegex.test(rawChildId)) {
console.warn("[uploads] invalid or missing childId:", rawChildId);
res.status(400).json({ message: "Parametre childId manquant ou invalide" });
return;
}
```
---
## 🔒 2. Suppression de secrets.json et migration vers .env
### Problème identifié
- Clé OpenAI stockée en clair dans `backend/src/data/secrets.json`
- Fichier commité dans Git (même si .gitignore l'exclut maintenant)
### Corrections appliquées
#### A. Fichier secrets.json supprimé
```bash
rm backend/src/data/secrets.json
```
#### B. Fichier .env créé
**Fichier**: `backend/.env`
```env
# API Keys
# ⚠️ IMPORTANT: CETTE CLÉ A ÉTÉ EXPOSÉE ET DOIT ÊTRE RÉVOQUÉE!
# 1. Aller sur https://platform.openai.com/api-keys
# 2. Révoquer la clé commençant par sk-proj-efaTQ8...
# 3. Créer une nouvelle clé
# 4. Remplacer la valeur ci-dessous
OPENAI_API_KEY=sk-proj-...
OPENAI_MODEL=gpt-4o
```
#### C. Code déjà configuré pour lire depuis .env
Le code existant dans `backend/src/services/secret-store.ts` et `backend/src/config/env.ts` lit déjà depuis les variables d'environnement.
### ⚠️ ACTION REQUISE PAR L'UTILISATEUR
**CRITIQUE**: La clé OpenAI a été exposée dans Git et doit être révoquée:
1. **Aller sur**: https://platform.openai.com/api-keys
2. **Révoquer**: La clé commençant par `sk-proj-efaTQ8cicJYU7k8RG...`
3. **Créer**: Une nouvelle clé API
4. **Remplacer**: La valeur dans `backend/.env`
---
## 🌐 3. Sécurisation du Service d'Ingestion
### Problèmes identifiés
- CORS permissif (`allow_origins=["*"]`)
- Endpoint `/config/openai` expose les clés en production
### Corrections appliquées
**Fichier**: `ingestion-service/src/ingestion/main.py`
#### A. CORS restreint par environnement
```python
_env = os.getenv("NODE_ENV", "development")
_allowed_origins = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:5173,http://localhost:5000").split(",")
if _env == "production":
# Production: strict CORS
app.add_middleware(
CORSMiddleware,
allow_origins=_allowed_origins,
allow_methods=["GET", "POST"],
allow_headers=["Content-Type"],
allow_credentials=False,
)
else:
# Development: permissive
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
```
#### B. Endpoint /config/openai désactivé en production
```python
@app.post("/config/openai")
async def set_openai_config(api_key: str = Body(..., embed=True), model: str | None = Body(None)) -> dict:
if _env == "production":
raise HTTPException(
status_code=403,
detail="Configuration endpoint disabled in production. Use environment variables instead."
)
# ... reste du code
```
---
## 🧹 4. Nettoyage du Repository
### Problèmes identifiés
- Fichiers `.js` dupliqués dans `frontend/src/` (générés par compilation)
- Fichiers `*.bak`, `*.bak2`, `*.bak3`
- `dist/` potentiellement commité
- Données PII dans `backend/src/data/client.json` et fichiers uploadés
### Corrections appliquées
#### A. Suppression des fichiers obsolètes
```bash
# Suppression de tous les .js dans frontend/src/
find family-planner/frontend/src -name "*.js" -type f -delete
# Suppression de tous les .bak*
find family-planner -name "*.bak*" -type f -delete
```
#### B. Mise à jour du .gitignore
**Fichier**: `.gitignore`
Ajouté:
```gitignore
# Data files with PII (Personally Identifiable Information)
backend/src/data/client.json
**/public/plans/
**/public/avatars/
```
Les lignes suivantes étaient déjà présentes:
- `dist/` (ligne 7)
- `backend/src/data/secrets.json` (ligne 29)
- `*.bak`, `*.backup` (lignes 36-37)
---
## 📊 Statistiques
### Fichiers modifiés
- `backend/src/routes/uploads.ts` - Sécurisation upload
- `backend/src/services/file-db.ts` - Ajout type personalLeaves
- `backend/src/services/personal-leave-service.ts` - Correction imports
- `backend/.env` - Nouveau fichier de configuration
- `ingestion-service/src/ingestion/main.py` - CORS et endpoints
- `frontend/src/state/ChildrenContext.tsx` - Ajout schoolRegion type
- `.gitignore` - Protection données PII
### Fichiers supprimés
- `backend/src/data/secrets.json` (1 fichier)
- Tous les `.js` dans `frontend/src/` (35 fichiers)
- Tous les `.bak*` (3 fichiers)
### Tests
- ✅ Backend compile sans erreur (`npm run build`)
- ✅ Frontend compile sans erreur (`npm run build`)
---
## 📝 Recommandations futures
### Court terme (Sprint actuel)
1. ✅ Révoquer la clé OpenAI exposée
2. ⏳ Ajouter tests backend pour routes critiques (supertest)
3. ⏳ Implémenter rate limiting par IP pour uploads
### Moyen terme (2-3 sprints)
1. Ajouter authentification utilisateur (JWT)
2. Implémenter encryption au repos pour données PII
3. Ajouter logs d'audit pour opérations sensibles
### Long terme (Roadmap)
1. Migration vers base de données (SQLite → Postgres)
2. Intégration avec solution de secrets management (Azure Key Vault, AWS Secrets Manager)
3. Mise en place CI/CD avec scans de sécurité automatiques
---
## ✅ Checklist de déploiement
Avant de déployer en production:
- [ ] Révoquer ancienne clé OpenAI
- [ ] Créer nouvelle clé OpenAI
- [ ] Configurer variable `OPENAI_API_KEY` en production
- [ ] Configurer variable `NODE_ENV=production` pour ingestion service
- [ ] Configurer variable `ALLOWED_ORIGINS` avec domaines production
- [ ] Vérifier que `.env` n'est PAS commité
- [ ] Vérifier que `dist/` n'est PAS commité
- [ ] Vérifier que `client.json` n'est PAS commité
- [ ] Tester upload avec fichiers valides et invalides
- [ ] Tester que `/config/openai` retourne 403 en production
---
**Date**: 2025-10-12
**Auteur**: Claude (Assistant IA)
**Validé par**: Philippe H.