# TP gestionnaire de mots de passe sécurisé ## Objectifs * Comprendre l'authentification moderne (JWT stocké en **Cookie HttpOnly** vs LocalStorage). * Manipuler des primitives cryptographiques (AES-256-GCM, Argon2). * Implémenter les opérations CRUD sécurisées où le serveur chiffre/déchiffre les données. * **Analyse critique** : identifier les limites de sécurité d'une architecture "Server-Side Encryption". ## Structure du projet Le projet est constitué de : 1. `server.js` : L'API Node.js (Express + SQLite). C'est ici que vous allez travailler. 2. `db_final.sqlite` : La base de données (générée automatiquement). 3. `vanilla_client/` : Le client front-end (fourni, ne pas modifier). Le schéma général de l'application est le suivant : ```mermaid flowchart LR Client["Browser (client vanilla_client/)"] -- "Requêtes http (api/...)" --> Server("Serveur (server.js)") Server -- "Cookie HttpOnly (JWT)" --> Client Server -- "Requêtes sql (lecture/écriture)" --> DB[("Base de données (db_final.sqlite)")] ``` ----- ## Comment lancer le projet 1. Ouvrez le dossier dans votre terminal. 2. Installez les dépendances : ```bash npm install ``` 3. Lancez le serveur : ```bash nodemon server.js ``` 4. Pour le client : clic droit sur `vanilla_client/index.html` \> "Run". ----- ## Étape 1 : analyse de l'existant (lecture de code) Avant de coder, analysez le fichier `server.js`, spécifiquement les parties **Configuration**, **Middleware** et les routes `/register` et `/login`. Répondez (pour vous-mêmes ou sur papier) aux questions suivantes pour comprendre le flux : 1. **Le secret du coffre :** * Lors du `/register`, nous générons une `userVaultKey` aléatoire. C'est la clé qui chiffrera tous les mots de passe de l'utilisateur. * Cette clé n'est **jamais** stockée en clair dans la BDD. Comment est-elle protégée avant d'être insérée dans la table `users` ? ```mermaid sequenceDiagram participant Client participant Serveur participant BDD Client->>Serveur: POST /register (login, password) activate Serveur Serveur->>Serveur: Hash password "ex: Argon2(password)" Serveur->>Serveur: Génère userVaultKey "clé aléatoire" Serveur->>Serveur: Chiffre vaultKey "AES(vaultKey, SERVER_MASTER_KEY)" Serveur->>BDD: INSERT users (login, hash, encryptedVaultKey) activate BDD BDD-->>Serveur: Utilisateur créé deactivate BDD Serveur-->>Client: Réponse 201 "utilisateur créé" deactivate Serveur ``` 2. **Le JWT "Stateful" :** * Lors du `/login`, le serveur récupère la `userVaultKey` de l'utilisateur. * Où le serveur stocke-t-il cette clé pour que l'utilisateur puisse s'en servir lors des requêtes suivantes (ex: ajouter un mot de passe) ? * Pourquoi chiffrons-nous cette clé avec `SERVER_MASTER_KEY` avant de la mettre dans le JWT ? ```mermaid sequenceDiagram participant Client participant Serveur participant BDD Client->>Serveur: "POST /login (login, password)" activate Serveur Serveur->>BDD: "SELECT * FROM users WHERE login = ?" activate BDD BDD-->>Serveur: "Données utilisateur (hash, encryptedVaultKey)" deactivate BDD Serveur->>Serveur: "Vérifie hash (Argon2(password) vs hash)" alt "Mot de passe correct" Serveur->>Serveur: "Déchiffre vaultKey (AES(encryptedVaultKey, MASTER_KEY))" Serveur->>Serveur: "Crée JWT (payload: { userId, decryptedVaultKey })" Serveur-->>Client: "Réponse 200 OK (Set-Cookie: token=JWT HttpOnly)" else "Mot de passe incorrect" Serveur-->>Client: "Réponse 401 (non autorisé)" end deactivate Serveur ``` 3. **Sécurité du Token :** * Regardez la méthode `res.cookie` dans `/login`. Pourquoi l'option `httpOnly: true` est-elle cruciale ici ? Le JavaScript côté client (ex: `script.js`) peut-il lire ce cookie ? ----- ## Étape 2 : implémentation (à vous de jouer) Le serveur démarre, vous pouvez vous inscrire et vous connecter. Cependant, les fonctionnalités liées aux mots de passe renvoient des erreurs ou sont vides. Vous devez compléter les **3 routes manquantes** dans `server.js` (cherchez les commentaires `TODO`). ### Mission A : sauvegarder une entrée (`POST /entries`) **Objectif :** chiffrer le mot de passe reçu avant de l'insérer en base. * **Aide :** * Le middleware `authenticateAndUnpackKey` a déjà fait le travail difficile. Il a déchiffré la clé de l'utilisateur et l'a placée dans `req.user.vaultKey`. * Utilisez la fonction utilitaire `encryptAES(texte, clé)` définie plus haut dans le fichier. * Regardez la structure de la table `entries` pour savoir quoi insérer (notamment `encrypted_blob`, `iv`, `auth_tag`). *Voici le flux d'une requête authentifiée que vous devez implémenter :* ```mermaid sequenceDiagram participant Client participant Serveur participant BDD Client->>Serveur: POST /entries (payload: { title, ... }, cookie: token) activate Serveur Note over Serveur: Middleware "authenticateAndUnpackKey" Serveur->>Serveur: Vérifie le JWT (reçu du cookie) Serveur->>Serveur: Extrait payload "{ userId, decryptedVaultKey }" Serveur->>Serveur: Stocke "decryptedVaultKey" dans "req.user.vaultKey" Note over Serveur: Exécution de la route (Mission A) Serveur->>Serveur: Chiffre le mot de passe "AES(data, req.user.vaultKey)" Serveur->>BDD: INSERT entries (owner_id, encrypted_blob, iv, tag) activate BDD BDD-->>Serveur: Entrée sauvegardée deactivate BDD Serveur-->>Client: Réponse 201 "créé" deactivate Serveur ``` ### Mission B : déchiffrer un mot de passe (`GET /entries/:id/password`) **Objectif :** Renvoyer le mot de passe en clair à l'utilisateur lorsqu'il clique sur l'icône "Oeil". * **Aide :** * Récupérez l'entrée en BDD via son `id`. * **Sécurité :** Assurez-vous dans la clause `WHERE` que l'entrée appartient bien à l'utilisateur connecté (`owner_id`). * Utilisez `decryptAES(...)` avec la `req.user.vaultKey` pour retrouver le texte clair. ### Mission C : lister les entrées (`GET /entries`) **Objectif :** Renvoyer la liste des sites pour le tableau de bord. Aucun mot de passe ne doit être inclus. * **Aide :** * Faites un `SELECT` classique. * **Performance & Sécurité :** Ne renvoyez **JAMAIS** le champ `encrypted_blob` (le mot de passe chiffré) ou les IV/Tags dans cette liste. On ne veut que les métadonnées (`id`, `title`, `url`, `username_field`) pour construire l'interface. Le déchiffrement se fera uniquement à la demande (Mission B). ----- ## Étape 3 : audit de sécurité (réflexion) Une fois votre code fonctionnel, prenez du recul. Dans cette architecture, le chiffrement se fait **Côté Serveur**. **Scénario catastrophe :** Imaginez qu'un attaquant (ou un employé malveillant) obtienne un accès complet à la machine qui héberge le serveur (accès aux fichiers et à la mémoire vive). 1. L'attaquant trouve le fichier `.env` (ou le code) contenant la variable `SERVER_MASTER_KEY`. 2. L'attaquant fait un "Dump" de la base de données `db_final.sqlite`. **Questions :** * L'attaquant peut-il déchiffrer les mots de passe stockés dans la table `entries` ? Si oui, expliquez comment il procèderait étape par étape. * **Architecture "Zero Knowledge"** : comment aurions-nous dû architecturer l'application pour que, même avec un accès total au serveur, l'attaquant ne puisse **jamais** lire les mots de passe des utilisateurs ? (Indice : Où devrait se faire le chiffrement AES ?) -----