Modèle de sécurité — Immbot AI
Authentification
- Sessions serveur : cookie
immbot_sessionhttpOnly,SameSite=Lax,Secureen production, identifiant aléatoire 256 bits, haché (SHA-256) avant stockage dans la tablesessions(un vol de la BD ne permet pas de rejouer une session). Expiration 14 jours glissants, révocation à la déconnexion et depuis l'admin. - Mots de passe : bcrypt (coût 12) via bcryptjs. Politique : ≥ 10 caractères.
admin123explicitement refusé comme mot de passe permanent. - Compte initial : créé par script de seed depuis
INITIAL_ADMIN_USERNAME/INITIAL_ADMIN_PASSWORD(défauts admin/admin123), champmust_change_password=1→ redirection forcée vers le changement de mot de passe, bannière d'avertissement tant que le mot de passe initial est actif.DISABLE_INITIAL_ADMIN=trueen production le désactive. - Limitation des tentatives : 8 échecs / 15 min par (IP + identifiant), à mémoire serveur +
journalisées dans
auth_events(connexions, échecs, changements de mot de passe, déconnexions). - Récupération : changement par l'utilisateur (mot de passe actuel requis) ; réinitialisation par l'admin (mot de passe temporaire + changement forcé). Pas de courriel en v1 (documenté).
- CSRF : mutations via
fetchmême origine + vérification systématique de l'en-têteOrigin/Sec-Fetch-Sitesur toutes les routes mutantes + cookies SameSite. Pas de formulaires cross-site. - SSO futur : la table
usersporteauth_provider(localaujourd'hui) et un identifiant externe nullable — l'ajout d'OIDC (Microsoft/Google/UQO) n'exige pas de migration.
Autorisation
- Rôles :
student<instructor<admin(l'admin a tout ; l'instructeur a la pédagogie et le contenu, pas la gestion des utilisateurs). - Contrôle d'accès par cours : table
enrollments; toute requête chat/RAG/apprentissage vérifie l'inscription au cours visé, côté serveur. - Espaces de connaissances : chaque fragment porte
space(official-imm1003,official-imm1033,student-temporary-upload,student-persistent-files,instructor-private,general-knowledge). Les requêtes étudiantes ne touchent jamaisinstructor-privateni les téléversements d'un autre étudiant (filtre SQL systématique, testé). - Routes
/adminet/api/admin/*: middleware + revérification du rôle dans chaque handler.
Secrets et données
OPENROUTER_API_KEYuniquement côté serveur (routes API) ; jamais dans un composant client, jamais journalisée..envgitignoré ;.env.examplesans valeurs réelles.- Journaux structurés sans secrets ni contenu de mot de passe ; les messages d'erreur client ne fuient pas les détails internes.
- Données étudiantes : minimisation (nom d'utilisateur, courriel optionnel) ; les statistiques professeur sont agrégées et anonymisées (seuil de 3 étudiants minimum avant affichage d'un agrégat) ; le contenu individuel n'est jamais montré.
submissions.dbdu prototype et le matériel d'examen : exclus /instructor-private(voir repository-analysis.md).- Sauvegardes :
scripts/backup.sh(copie datée dedata/hors uploads temporaires).
Défenses applicatives
- Validation zod de toutes les entrées API ; limites de taille (messages 32 k, uploads 25 Mo, types MIME vérifiés par signature).
- Requêtes SQL exclusivement préparées (aucune concaténation).
- Rendu Markdown sans HTML brut (
skipHtml) → pas de XSS via réponses de modèle. - En-têtes : CSP (script-src 'self'), X-Content-Type-Options, Referrer-Policy, X-Frame-Options DENY.
- Injection de prompt : le contenu récupéré et les fichiers étudiants sont encadrés de délimiteurs et le prompt système précise qu'ils sont des données, pas des instructions ; la validation de citations empêche l'exfiltration de fragments non autorisés (le serveur ne fournit au modèle que les fragments auxquels l'utilisateur a déjà droit).
- Limites d'usage : budgets quotidiens/mensuels par utilisateur et globaux, appliqués côté serveur avant chaque appel modèle.
Journalisation et audit
auth_events (sécurité), usage_log (appels modèles : utilisateur, modèle, jetons, coût, latence),
ingestion_runs (contenu), report_flags (réponses signalées). Consultables dans l'admin.