Files
ENI-JSAdvanced_14/README.md
2025-12-18 15:28:26 +01:00

186 lines
7.8 KiB
Markdown

# 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` ?
<!-- end list -->
```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 ?
<!-- end list -->
```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 ?)
-----