Politique de Sécurité
Ce fichier est la référence sécurité de la plateforme. Il répond à trois questions : comment nous nous protégeons, quoi faire si quelque chose est compromis, et comment nous gardons les dépendances saines. C'est pour nous d'abord, pour un auditeur ensuite.
Surface d'attaque — Ce qui est exposé publiquement
Cette section documente exactement ce qui est accessible sans authentification :
| Endpoint / Ressource | Public | Authentifié | Admin uniquement |
|---|---|---|---|
POST /api/v1/auth/login | ✅ | — | — |
POST /api/v1/auth/register | ✅ | — | — |
GET /api/v1/internships | — | ✅ | — |
POST /api/v1/applications | — | ✅ (L3/M) | — |
GET /api/v1/admin/users | — | — | ✅ |
| Frontends (HTML/JS/CSS) | ✅ | — | — |
Règle : Tout ce qui n'est pas dans la colonne "Public" doit être refusé par le backend si le token est absent ou invalide. Le frontend n'est pas une protection.
Protection contre les attaques communes
Cette section documente chaque mesure en place et comment la vérifier :
Injection SQL
- Mesure : SQLAlchemy ORM avec requêtes paramétrées. Aucune requête SQL construite à la main avec des f-strings.
- Ce qui est interdit :
f"SELECT * FROM users WHERE email='{email}'"— jamais. - Comment vérifier : Grep le code pour
text((requêtes SQL raw dans SQLAlchemy) — chaque occurrence doit être justifiée et utiliser des paramètres bindés.
XSS (Cross-Site Scripting)
- Mesure : React échappe automatiquement le contenu des variables dans le JSX.
- Ce qui est interdit :
dangerouslySetInnerHTMLsans sanitization explicite. - Cookie httpOnly : Le refresh token est inaccessible au JavaScript — même si du JS malicieux s'exécute, il ne peut pas voler le token de session.
CSRF (Cross-Site Request Forgery)
- Mesure :
SameSite=Laxsur les cookies + validation de l'origine dans les headers CORS côté backend. - Comment ça protège : Un site tiers ne peut pas déclencher des requêtes authentifiées à notre API depuis le browser d'un user connecté.
CORS
- Mesure : Liste blanche explicite des origines autorisées dans FastAPI (jamais
allow_origins=["*"]avec credentials). - Où c'est configuré :
app/main.py, constanteALLOWED_ORIGINS. - Comment vérifier : Tester depuis une origine non listée → doit recevoir une erreur CORS.
Rate Limiting
- Mesure : Nous limitons les endpoints sensibles (login, register, forgot-password) pour bloquer le brute force.
- Outil recommandé :
slowapi(FastAPI) ou rate limiting au niveau du reverse proxy (Nginx/Railway). - Seuils minimaux : Login → 10 tentatives / minute / IP. Register → 5 / minute / IP.
- Nous documentons ici les seuils exacts configurés.
Mots de passe
- Mesure : Hachage avec
bcrypt(coût minimum 12). Jamais MD5, jamais SHA1, jamais en clair. - Aucun mot de passe ne doit apparaître dans les logs — vérifier que le schéma Pydantic de login exclut le password des logs.
Logs d'audit — Ce qui est tracé
Cette section documente quelles actions sont loggées et où :
| Action | Loggée ? | Où | Ce qui est enregistré |
|---|---|---|---|
| Login réussi | ✅ | DB table audit_logs | user_id, IP, timestamp |
| Login échoué | ✅ | DB table audit_logs | email tenté, IP, timestamp |
| Logout | ✅ | DB table audit_logs | user_id, timestamp |
| Action admin (suppression, modification) | ✅ | DB table audit_logs | admin_id, action, target_id, timestamp |
| Erreurs 500 | ✅ | Sentry | Stack trace complète |
Règle : Les logs d'audit ne doivent jamais contenir de données sensibles (mot de passe, token complet, données personnelles non nécessaires).
Durée de rétention : Définir ici combien de temps les logs sont conservés (ex : 90 jours).
Gestion des dépendances vulnérables
Le vrai risque silencieux : une lib que nous utilisons a une faille connue et nous ne le savons pas.
Procédure de vérification (à faire chaque mois)
Backend (Python) :
pip install pip-audit
pip-audit
# Liste les CVE connues dans nos dépendances
Frontend (Node.js) :
npm audit
# ou
npm audit --fix # Correction automatique des failles non-breaking
Règle de réponse selon la sévérité
| Sévérité | Délai de correction | Responsable |
|---|---|---|
| Critique (CVSS ≥ 9) | 24h maximum | Dev senior en priorité absolue |
| Haute (CVSS 7-9) | 1 semaine | Dev senior |
| Moyenne (CVSS 4-7) | Prochain sprint | N'importe quel dev |
| Faible (CVSS < 4) | Backlog | Traité quand possible |
Configurer GitHub Dependabot pour recevoir automatiquement des PRs quand une dépendance a une faille connue. C'est gratuit et ça prend 5 minutes à configurer.
Procédure d'incident — Si quelque chose est compromis
C'est le document le plus important à lire avant qu'un incident arrive, pas pendant.
Scénario 1 : Secret/credential exposé (API key, DB password dans un commit)
- Immédiatement (< 5 min) : Révoquer et régénérer le credential compromis — avant même de comprendre comment c'est arrivé
- Dans l'heure : Vérifier les logs pour détecter une utilisation non autorisée du credential
- Dans la journée : Identifier comment c'est arrivé, corriger le processus (
.gitignore, pre-commit hook, etc.) - Nous documentons : Ajouter un post-mortem dans
security/incidents/YYYY-MM-DD-description.md
Scénario 2 : Compte admin compromis
- Immédiatement : Changer le mot de passe du compte (la révocation de token n'est pas encore implémentée — voir lacunes connues — le token actif reste valide maximum 15 minutes jusqu'à expiration naturelle)
- Immédiatement : Examiner les logs d'audit pour toutes les actions effectuées par ce compte dans les 24-72h précédentes
Scénario 3 : Faille découverte dans notre code
- Ne pas patcher en prod directement — créer une branche
hotfix/ - Évaluer l'impact réel avant de paniquer : la faille est-elle exploitable ? A-t-elle été exploitée ?
- Patcher, tester sur staging, déployer
- Post-mortem obligatoire
Contacts d'urgence
Nous documentons ici (pas dans ce guide public, dans notre version interne) :
- Qui appeler en dehors des heures de bureau
- Accès Railway/Vercel en urgence (qui a les droits ?)
- Si obligation légale de notification : contact CNIL
RGPD — Données personnelles collectées
Documenter ce que nous stockons et pourquoi — utile si quelqu'un demande la suppression de ses données ou si la CNIL pose des questions.
| Donnée | Où stockée | Pourquoi | Durée de rétention |
|---|---|---|---|
DB users | Authentification | Jusqu'à suppression du compte | |
| Mot de passe (haché) | DB users | Authentification | Jusqu'à suppression du compte |
| CV (fichier) | Stockage fichiers | Candidature | Jusqu'à suppression du compte |
| Logs d'activité | DB audit_logs | Sécurité | 90 jours |
Procédure de suppression de compte : Nous documentons exactement quelles tables sont touchées quand un user demande la suppression de ses données (droit à l'effacement RGPD).
Ce qui n'est PAS encore en place (et c'est ok)
Être honnête sur ce qui manque évite de fausser un futur audit. Nous documentons les lacunes connues avec une date cible :
| Mesure | Statut | Priorité | Date cible |
|---|---|---|---|
| Pentest externe | ❌ Pas fait | Moyenne | Quand 500+ users actifs |
| 2FA pour les admins | ❌ Pas implémenté | Haute | Sprint 3 |
| Chiffrement des fichiers au repos | ❌ Pas fait | Moyenne | Phase 2 |
| Audit trail complet | 🟡 Partiel | Haute | Sprint 2 |
| Révocation immédiate des tokens | ❌ Pas implémenté | Haute | Sprint 2 |
| Tokens en mémoire JS (pas localStorage) | ❌ À corriger | Haute | Sprint 1 |
Révocation des tokens : Currently Redis est utilisé comme cache de performance, pas comme gestionnaire de sessions. Le JWT est la seule source de vérité — si un token est volé ou si un compte admin est compromis, il est impossible de révoquer l'accès avant l'expiration naturelle du token (15 min access, 7j refresh). Solution cible : stocker les sessions actives dans Redis et vérifier à chaque requête.
localStorage vs mémoire : L'access token est actuellement stocké dans localStorage côté frontend. localStorage est accessible par tout script JS sur la page — une faille XSS permettrait de voler tous les tokens. Solution cible : access token en mémoire JS, refresh token en cookie httpOnly uniquement.
Pourquoi documenter les lacunes ? Parce qu'un auditeur les trouvera de toute façon. Mieux vaut montrer que nous en sommes conscients et que nous avons un plan.
Ce qu'il ne faut PAS mettre ici
- ❌ Des credentials, tokens, ou mots de passe réels
- ❌ Des détails d'exploitation de failles (comment attaquer notre propre système en détail — ça va dans un document privé séparé si nécessaire)
- ❌ Des fausses assurances ("nous sommes totalement sécurisés") — personne ne l'est
Questions à se poser avant de valider ce document
- Si un incident arrive demain, l'équipe sait quoi faire sans lire autre chose ?
- Les lacunes connues sont-elles documentées honnêtement ?
- Les logs d'audit couvrent-ils toutes les actions sensibles ?