Chapitre 1227 minIntermédiaire
Épreuve blanche E7 n° 1 : un réseau de centres de rééducation
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- traiter un sujet complet de type 2025 en 4 heures ;
- comparer tes réponses à une correction détaillée ;
- mesurer ce qu'il te reste à travailler.
3 exercices corrigés
Cette épreuve blanche d'E7 SLAM reproduit un sujet complet, construit sur le modèle du sujet 2025 (cas Casterman) : trois dossiers, 80 points, un contexte unique et des documents à exploiter. Elle porte sur une application de santé, ce qui mobilise fortement le RGPD. Passe-la en 4 heures, sans document, et n'ouvre chaque correction qu'après avoir rédigé ta réponse sur une feuille. Les corrections sont des propositions détaillées, pas un corrigé officiel.
Comment l'utiliser
- Lis tout le sujet, contexte et documents compris, avant de répondre, puis fais ton plan de temps : 20 minutes en tout.
- Ton plan de temps : dossier A, 18 points, environ 45 minutes ; dossier B, 32 points, environ 1 h 25 ; dossier C, 30 points, environ 1 h 15 ; relecture, 15 minutes.
- Rédige toutes tes réponses, puis compare avec les corrections. Note-toi honnêtement : chaque élément de la correction que tu as écrit vaut une part des points.
Présentation du contexte
Kinéo Santé est un réseau de quatorze centres de rééducation dans l'ouest de la France. Ses 160 kinésithérapeutes et ses médecins accueillent chaque année environ 30 000 patients après une opération, un accident ou pour une maladie chronique. Chaque centre a son secrétariat ; un médecin-chef supervise plusieurs centres.
Le réseau utilise RééduPlan, une application Web développée en PHP avec une base MySQL 8.4, qui gère les dossiers de rééducation (bilans, séances, exercices) et le planning. Une application mobile permet aux patients de retrouver leurs exercices à domicile ; elle interroge une API REST écrite en Node.js. Un outil de bureau en Java, RééduAdmin, sert aux médecins-chefs et aux responsables de centre.
RééduPlan est développé et maintenu par l'entreprise de services du numérique Altiva Numérique, dans le cadre d'un contrat de tierce maintenance applicative. Tu fais partie de l'équipe de développement d'Altiva. Ton chef de projet, M. Ferrand, te confie trois missions à la suite d'un audit de sécurité commandé par la directrice des systèmes d'information de Kinéo Santé, Mme Ollivier.
| Dossier | Thème | Points |
|---|---|---|
| A | Analyse des risques et protection des données de santé | 18 |
| B | Sécurisation de l'authentification et des habilitations | 32 |
| C | Sécurisation de l'API mobile et journalisation des accès | 30 |
| Total | 80 |
Document commun : extrait du schéma de la base de RééduPlan
Centre (id, ville, adresse)
Compte (id, login, empreinte, role, idCentre)
role : 'kine', 'medecin-chef', 'secretariat', 'patient'
idCentre : clé étrangère en référence à id de Centre
Patient (id, nom, prenom, dateNaissance, telephone, mel, numeroSecu, idCentre, idReferent)
idCentre : clé étrangère en référence à id de Centre
idReferent : clé étrangère en référence à id de Compte (kinésithérapeute référent)
Bilan (id, idPatient, dateBilan, pathologie, contenu, idPraticienModif)
idPraticienModif : identifiant du dernier praticien ayant modifié le bilan, renseigné par l'application
Seance (id, dateHeure, statut, idPatient, idPraticien, idCentre)
statut : 'PREVUE', 'REALISEE', 'ANNULEE'
Exercice (id, idPatient, titre, consignes, urlVideo)
JournalAcces (id, idCompte, idPatient, action, dateHeure, adresseIP)
Dossier A : analyse des risques et protection des données de santé (18 points)
Dans le cadre de l'audit, l'équipe d'Altiva participe à un atelier d'analyse de risques. Trois récits utilisateurs ont été évalués selon les critères de disponibilité (D), d'intégrité (I), de confidentialité (C) et de preuve (P). L'échelle comporte trois niveaux : sans objet, modéré, important.
Document A1 : besoins de sécurité de trois récits utilisateurs
| Récit | D | I | C | P |
|---|---|---|---|---|
| R1 – En tant que kinésithérapeute, je consulte le bilan de rééducation d'un patient (pathologie, observations, objectifs) | modéré | important | important | modéré |
| R2 – En tant que secrétaire, je planifie les séances de la journée du centre | important | modéré | modéré | sans objet |
| R3 – En tant que patient, je consulte chez moi la vidéo de mes exercices | modéré | modéré | modéré | sans objet |
Document A2 : note de Mme Ollivier
« Pour réduire nos coûts, nous envisageons de faire héberger la base de RééduPlan par un fournisseur d'informatique en nuage. Je souhaite connaître les obligations réglementaires particulières liées à nos données. Par ailleurs, notre portail patient a subi la semaine dernière plusieurs milliers de tentatives de connexion avec des couples identifiant et mot de passe provenant manifestement de fuites d'autres sites. »
Exercice 1 sur 3 Dossier A : les questions Réussi À revoir
Mission A1 – Évaluer les besoins de sécurité
- A1.1 Justifier le niveau important du critère de confidentialité du récit R1.
- A1.2 Justifier la différence de niveau de disponibilité entre les récits R2 et R3.
Mission A2 – Protéger les données de santé
- A2.1 À partir du schéma commun, lister les données à caractère personnel de la table
Patientet de la tableBilan, puis indiquer lesquelles sont des données sensibles au sens du RGPD. Justifier. - A2.2 Indiquer deux obligations réglementaires que Kinéo Santé doit respecter avant de confier l'hébergement de la base à un fournisseur en nuage.
Mission A3 – Traiter un événement redouté
- A3.1 Nommer l'attaque décrite dans le document A2 et expliquer pourquoi elle peut réussir alors que RééduPlan n'a jamais été piraté.
- A3.2 Proposer deux mesures techniques pour s'en protéger, en justifiant chacune.
Voir la réponseMasquer la réponse de l'exercice 1
A1.1 (confidentialité de R1). Le bilan contient la pathologie du patient, ses observations cliniques et ses objectifs de rééducation : ce sont des données de santé, donc des données sensibles au sens de l'article 9 du RGPD. Leur divulgation porterait atteinte à la vie privée du patient (un employeur ou un assureur qui apprendrait une pathologie, par exemple) et engagerait la responsabilité de Kinéo Santé, tenue au secret médical, avec un risque de sanction de la CNIL. Le niveau important est justifié.
A1.2 (disponibilité de R2 et R3). Si le planning du jour est indisponible (R2), le secrétariat ne sait plus quels patients arrivent, avec quel praticien et dans quelle salle : l'activité du centre est désorganisée immédiatement, des séances sont perdues ou doublonnées, et il n'existe pas de solution de repli simple. Le besoin est important. Si la vidéo d'exercices est indisponible (R3), le patient peut reporter sa séance à domicile ou suivre la fiche papier remise en séance ; la gêne est réelle mais différable, sans conséquence immédiate sur la prise en charge. Le besoin est modéré.
A2.1 (données personnelles et sensibles).
- Table
Patient:id,nom,prenom,dateNaissance,telephone,meletnumeroSecusont des données à caractère personnel ;idCentreetidReferent, rattachés à un patient identifié, le sont aussi indirectement. - Table
Bilan: toutes les colonnes rattachées à un patient (idPatient,dateBilan,pathologie,contenu) sont personnelles ;idPraticienModifest une donnée personnelle du praticien. - Données sensibles :
pathologieetcontenudu bilan, qui sont des données concernant la santé (article 9 du RGPD). Par extension, le simple fait d'être patient d'un centre de rééducation révèle une information de santé. - Le numéro de sécurité sociale (NIR) n'est pas une donnée sensible au sens de l'article 9, mais son usage est strictement encadré en France : la CNIL le considère comme une donnée particulière à protéger. Le mentionner est un plus.
A2.2 (hébergement). Deux obligations parmi :
- Recourir à un hébergeur certifié HDS (hébergeur de données de santé) : le Code de la santé publique (article L1111-8) impose cette certification à quiconque héberge des données de santé recueillies lors d'activités de soins pour le compte d'un tiers.
- Réaliser une analyse d'impact relative à la protection des données (AIPD) avant le traitement (article 35 du RGPD) : il s'agit de données sensibles traitées à grande échelle.
- Encadrer la sous-traitance par un contrat qui fixe les obligations de l'hébergeur en matière de sécurité, de confidentialité et d'assistance en cas de violation (article 28 du RGPD).
- Vérifier la localisation des données et, s'il y a des transferts hors de l'Union européenne, leur encadrement.
A3.1 (l'attaque). C'est une attaque par bourrage d'identifiants (credential stuffing) : l'attaquant essaie automatiquement des couples identifiant et mot de passe volés sur d'autres sites. Elle réussit parce que beaucoup d'utilisateurs réutilisent le même mot de passe sur plusieurs services : RééduPlan n'a pas besoin d'avoir été piraté, il suffit qu'un patient ait utilisé le même couple sur un site qui, lui, a fui.
A3.2 (deux mesures). Deux parmi :
- Authentification multifacteur : même avec le bon mot de passe, l'attaquant ne possède pas le second facteur (code envoyé ou généré sur le téléphone du patient).
- Limitation du nombre de tentatives par compte et par adresse IP, avec temporisation croissante ou blocage temporaire : l'automatisation de milliers d'essais devient impossible.
- Détection et alerte sur le volume d'échecs de connexion, pour réagir pendant l'attaque.
- Vérification des mots de passe contre les listes de mots de passe compromis à la création et au changement, avec une politique de longueur conforme aux recommandations de la CNIL.
Tu avais trouvé ?
Dossier B : sécurisation de l'authentification et des habilitations (32 points)
Mission B1 – Renforcer l'authentification. L'audit a relevé un extrait de la table Compte et le code de la page de connexion.
Document B1 : extrait de la table Compte
| id | login | empreinte | role |
|---|---|---|---|
| 11 | apetit | 44b845ea2e47e98aab01e39049beb9ce… | kine |
| 12 | bnguyen | 75c288429cbfe354640ed183db3d9170… | kine |
| 13 | cmorel | 44b845ea2e47e98aab01e39049beb9ce… | kine |
Les empreintes sont calculées avec SHA-256 et tronquées ici à 32 caractères sur 64.
Document B2 : extrait de connexion.php
1. session_start();
2. $login = $_POST['login'];
3. $empreinte = hash('sha256', $_POST['mdp']);
4. $sql = "SELECT id, role, idCentre FROM Compte
5. WHERE login = :login AND empreinte = :empreinte";
6. $stmt = $pdo->prepare($sql);
7. $stmt->execute([':login' => $login, ':empreinte' => $empreinte]);
8. $compte = $stmt->fetch(PDO::FETCH_ASSOC);
9. if ($compte) {
10. $_SESSION['idCompte'] = $compte['id'];
11. $_SESSION['role'] = $compte['role'];
12. $_SESSION['idCentre'] = $compte['idCentre'];
13. header('Location: accueil.php');
14. } else {
15. header('Location: index.php?erreur=1');
16. }
Mission B2 – Tracer les modifications des bilans. Pour des raisons de preuve, chaque modification du contenu d'un bilan doit être historisée dans la table HistoBilan (id, idBilan, contenuPrecedent, dateModif, auteur), avec la version précédente du contenu et l'identifiant du praticien qui a fait la modification. Un déclencheur a été écrit, mais l'historique est inutilisable.
Document B3 : déclencheur apres_modif_bilan
1. CREATE TRIGGER apres_modif_bilan
2. AFTER UPDATE ON Bilan FOR EACH ROW
3. BEGIN
4. IF OLD.contenu <> NEW.contenu THEN
5. INSERT INTO HistoBilan (idBilan, contenuPrecedent, dateModif, auteur)
6. VALUES (NEW.id, NEW.contenu, NOW(), CURRENT_USER());
7. END IF;
8. END
RééduPlan se connecte à la base avec un compte unique, reeduplan_app. La fonction MySQL CURRENT_USER() renvoie le compte MySQL de la connexion en cours.
Mission B3 – Contrôler l'accès aux dossiers dans RééduAdmin. Dans l'outil Java, un praticien peut consulter le dossier d'un patient si l'une de ces conditions est vraie : il est médecin-chef ; il est le kinésithérapeute référent du patient, quel que soit son centre ; il est kinésithérapeute dans le centre du patient.
Document B4 : classes métier (extrait)
Centre : int id ; getId()
Praticien : int id ; String role ; Centre leCentre ; getId(), getRole(), getLeCentre()
Patient : int id ; Centre leCentre ; Praticien leReferent (peut être null) ;
getLeCentre(), getLeReferent()
Document B5 : tests unitaires de peutConsulter
@BeforeEach
void setUp() {
rennes = new Centre(1, "Rennes"); nantes = new Centre(2, "Nantes");
kineRennes = new Praticien(11, "kine", rennes);
kineNantes = new Praticien(13, "kine", nantes);
chef = new Praticien(14, "medecin-chef", nantes);
patientRennes = new Patient(201, rennes, null);
patientNantes = new Patient(203, nantes, kineRennes);
}
@Test void test1() { assertTrue(kineRennes.peutConsulter(patientRennes)); }
@Test void test2() { assertFalse(kineNantes.peutConsulter(patientRennes)); }
@Test void test3() { assertTrue(kineRennes.peutConsulter(patientNantes)); }
@Test void test4() { assertTrue(chef.peutConsulter(patientRennes)); }
Mission B4 – Fournir des statistiques sans données personnelles. Le service qualité de Kinéo Santé veut suivre le nombre de séances réalisées par centre et par mois, depuis son serveur de tableaux de bord (10.8.0.12). Il ne doit accéder à aucune donnée personnelle.
Exercice 2 sur 3 Dossier B : les questions Réussi À revoir
- B1.1 Indiquer ce que révèle l'extrait de la table
Compte(document B1) et expliquer pourquoi le stockage actuel des mots de passe est insuffisant. - B1.2 Modifier le code de connexion.php pour utiliser des empreintes produites par
password_hash()avec l'algorithme Argon2id, et renforcer la session après authentification. Indiquer seulement les lignes ajoutées ou modifiées, avec leurs numéros. - B1.3 Un collègue affirme que connexion.php est vulnérable à l'injection SQL. Indiquer s'il a raison, en justifiant.
- B2.1 Expliquer pourquoi l'historique produit par le déclencheur ne permet pas de retrouver la version précédente d'un bilan.
- B2.2 Expliquer pourquoi la colonne
auteurdeHistoBilancontient toujours la même valeur. - B2.3 Corriger le déclencheur. Indiquer les numéros des lignes modifiées.
- B3.1 Expliquer les cas testés par les méthodes de test du document B5. Indiquer un cas important qui n'est pas testé.
- B3.2 Écrire la méthode
boolean peutConsulter(Patient unPatient)de la classePraticien. - B4.1 Écrire les requêtes qui créent une vue
v_stats_seances(centre, mois au format AAAA-MM, nombre de séances réalisées), créent un comptestatspour le serveur du service qualité et lui donnent uniquement le droit de lire cette vue. La base s'appellereeduplan.
Voir la réponseMasquer la réponse de l'exercice 2
B1.1. Les comptes apetit et cmorel ont la même empreinte : ils ont donc le même mot de passe. Un attaquant qui obtient la table le voit immédiatement et sait qu'en trouvant un mot de passe, il en a deux. Le stockage est insuffisant pour deux raisons : SHA-256 est une fonction rapide, qu'un processeur graphique calcule des milliards de fois par seconde, ce qui rend l'attaque par dictionnaire efficace ; et il n'y a pas de sel, donc deux mots de passe identiques donnent la même empreinte et les tables précalculées fonctionnent. C'est une défaillance cryptographique (A02:2021). Il faut une fonction lente et salée : bcrypt ou Argon2.
B1.2.
- Ligne 3 : supprimée. Le mot de passe n'est plus haché avant la requête : la vérification se fait après, en PHP.
- Lignes 4 et 5 (modifiées) : on cherche le compte par son identifiant seul, puisqu'une empreinte salée ne peut plus être comparée en SQL.
4. $sql = "SELECT id, role, idCentre, empreinte FROM Compte
5. WHERE login = :login";
- Ligne 7 (modifiée) :
$stmt->execute([':login' => $login]); - Ligne 9 (modifiée) :
if ($compte && password_verify($_POST['mdp'], $compte['empreinte'])) { - Après la ligne 9 (ajout, 9a) :
session_regenerate_id(true);
password_verify relit l'algorithme, le coût et le sel dans l'empreinte stockée. session_regenerate_id(true) attribue un nouvel identifiant de session après la connexion, ce qui empêche la fixation de session. À l'inscription et au changement de mot de passe, l'empreinte est calculée par password_hash($mdp, PASSWORD_ARGON2ID). Les empreintes SHA-256 existantes doivent être migrées : par exemple, forcer la réinitialisation des mots de passe, ou recalculer l'empreinte au premier login réussi.
B1.3. Il a tort. La requête utilise des marqueurs nommés (:login, :empreinte) et les valeurs sont transmises séparément par execute() : une saisie ne peut pas modifier la structure de la requête. Le problème de cette page est le hachage, pas l'injection.
B2.1. Ligne 6, le déclencheur enregistre NEW.contenu, c'est-à-dire le contenu après modification, dans la colonne contenuPrecedent. L'ancienne version, OLD.contenu, n'est enregistrée nulle part : elle est perdue à chaque modification, et l'historique ne contient que des copies du contenu actuel.
B2.2. CURRENT_USER() renvoie le compte MySQL de la connexion. Or RééduPlan se connecte toujours avec le même compte, reeduplan_app : la colonne contient donc reeduplan_app@localhost pour toutes les modifications, quel que soit le praticien. L'historique ne permet pas d'imputer une modification à une personne, ce qui ruine le critère de preuve. L'identité du praticien est connue de l'application, qui la renseigne dans Bilan.idPraticienModif.
B2.3. Ligne 6 (modifiée) :
6. VALUES (NEW.id, OLD.contenu, NOW(), NEW.idPraticienModif);
Amélioration possible de la ligne 4 : IF NOT (OLD.contenu <=> NEW.contenu) THEN. L'opérateur <=> compare aussi correctement les valeurs NULL, alors que <> renvoie NULL (donc faux) si l'un des contenus vaut NULL.
B3.1.
test1: un kinésithérapeute consulte un patient de son propre centre : autorisé.test2: un kinésithérapeute consulte un patient d'un autre centre dont il n'est pas le référent : refusé.test3: un kinésithérapeute consulte un patient d'un autre centre dont il est le référent : autorisé.test4: un médecin-chef consulte un patient d'un autre centre : autorisé.
Cas non testé : un compte qui n'est ni kinésithérapeute ni médecin-chef, par exemple une secrétaire du centre du patient, doit être refusé. Sans ce test, une méthode qui autoriserait tout le centre, quel que soit le rôle, passerait les quatre tests.
B3.2.
public boolean peutConsulter(Patient unPatient) {
if (this.role.equals("medecin-chef")) {
return true;
}
if (unPatient.getLeReferent() != null
&& unPatient.getLeReferent().getId() == this.id) {
return true;
}
return this.role.equals("kine")
&& unPatient.getLeCentre().getId() == this.leCentre.getId();
}
Le test getLeReferent() != null évite une NullPointerException pour un patient sans référent. Les chaînes se comparent avec equals, les identifiants entiers avec ==. La dernière condition vérifie le rôle et le centre : une secrétaire du même centre est refusée.
B4.1.
CREATE VIEW reeduplan.v_stats_seances AS
SELECT idCentre,
DATE_FORMAT(dateHeure, '%Y-%m') AS mois,
COUNT(*) AS nbSeances
FROM reeduplan.Seance
WHERE statut = 'REALISEE'
GROUP BY idCentre, DATE_FORMAT(dateHeure, '%Y-%m');
CREATE USER 'stats'@'10.8.0.12' IDENTIFIED BY 'mot_de_passe_long_et_aleatoire';
GRANT SELECT ON reeduplan.v_stats_seances TO 'stats'@'10.8.0.12';
La vue ne renvoie que des agrégats par centre et par mois : aucune donnée personnelle. Le compte n'est utilisable que depuis le serveur du service qualité et ne peut lire que la vue ; la vue s'exécutant avec les droits de son créateur, il n'a besoin d'aucun droit sur la table Seance. C'est l'application du moindre privilège.
Tu avais trouvé ?
Dossier C : sécurisation de l'API mobile et journalisation des accès (30 points)
Mission C1 – Sécuriser l'API de l'application mobile. L'application mobile des patients s'authentifie auprès de l'API et reçoit un jeton JWT. Le middleware exigerJeton vérifie la signature et l'expiration du jeton, puis place sa charge utile dans req.utilisateur. Lors de l'audit, un testeur connecté avec le compte du patient 2207 a obtenu les exercices du patient 2208 en appelant GET /api/patients/2208/exercices.
Document C1 : route des exercices (Node.js, Express)
1. router.get('/api/patients/:id/exercices', exigerJeton, async (req, res) => {
2. const idPatient = Number(req.params.id);
3. const [lignes] = await db.execute(
4. 'SELECT titre, consignes, urlVideo FROM Exercice WHERE idPatient = ?',
5. [idPatient]);
6. res.json(lignes);
7. });
Document C2 : charge utile décodée d'un jeton d'accès du patient 2207
{ "sub": "2207", "role": "patient", "iat": 1806000000, "exp": 1806000900 }
iat est la date d'émission et exp la date d'expiration, en secondes depuis le 1er janvier 1970. L'application reçoit aussi un jeton de rafraîchissement valable 30 jours.
Mission C2 – Journaliser les accès aux dossiers. Kinéo Santé veut tracer chaque consultation et chaque modification d'un dossier patient dans RééduPlan, conformément aux recommandations de la CNIL.
Document C3 : méthode de journalisation à compléter (PHP)
1. /**
2. * Enregistre un accès à un dossier patient
3. * @param int $idCompte le compte connecté
4. * @param int $idPatient le patient dont le dossier est ouvert
5. * @param string $action 'consultation' ou 'modification'
6. */
7. public function journaliserAcces(int $idCompte, int $idPatient, string $action): void {
8. $sql = "INSERT INTO JournalAcces (idCompte, idPatient, action, dateHeure)
9. VALUES (:compte, :patient, :action, NOW())";
10. $stmt = $this->pdo->prepare($sql);
11. $stmt->execute([':compte' => $idCompte, ':patient' => $idPatient,
12. ':action' => $action]);
13. }
Exercice 3 sur 3 Dossier C : les questions Réussi À revoir
- C1.1 Nommer la faille constatée par le testeur, en la situant dans l'OWASP Top 10 2021, et expliquer sa cause dans le code du document C1.
- C1.2 Modifier la route pour corriger la faille. Indiquer les lignes ajoutées.
- C1.3 Indiquer le code de statut HTTP que l'API doit renvoyer dans chacun des cas suivants : a) aucun jeton ; b) jeton valide du patient 2207 qui demande les exercices du patient 2208 ; c) jeton du patient 2207 expiré.
- C1.4 Calculer la durée de validité du jeton d'accès du document C2. Justifier qu'elle soit courte alors que le jeton de rafraîchissement vaut 30 jours.
- C2.1 Compléter la méthode
journaliserAccespour enregistrer l'adresse IP du poste, en respectant les bonnes pratiques. Indiquer les lignes ajoutées ou modifiées. La tableJournalAccespossède une colonneadresseIP. - C2.2 Un collègue propose d'ajouter au journal le contenu du bilan consulté, « pour avoir une trace complète ». Indiquer pourquoi ce n'est pas une bonne pratique.
- C2.3 Écrire la requête qui donne, pour le 8 mars 2027, le login des comptes ayant consulté plus de 3 dossiers distincts de patients rattachés à un autre centre que le leur, avec ce nombre de dossiers.
- C2.4 Exécutée sur les données du 8 mars 2027, la requête renvoie deux lignes :
apetit | 4(kinésithérapeute à Rennes) etdsimon | 4(médecin-chef à Nantes). Analyser ce résultat. - C2.5 Indiquer la durée de conservation de ces journaux recommandée par la CNIL et deux mesures pour garantir leur intégrité.
Voir la réponseMasquer la réponse de l'exercice 3
C1.1. C'est un contrôle d'accès défaillant (A01:2021), sous la forme d'une référence directe non sécurisée à un objet (IDOR). Le middleware authentifie bien le patient 2207, mais la route utilise l'identifiant fourni dans l'URL (ligne 2) sans vérifier qu'il correspond au patient connecté. Tout patient authentifié peut lire les exercices de n'importe quel autre en changeant ce numéro ; or les exercices révèlent une information de santé.
C1.2. Après la ligne 2 (ajout, 2a à 2c) :
2a. if (req.utilisateur.role !== 'patient' || Number(req.utilisateur.sub) !== idPatient) {
2b. return res.status(403).json({ erreur: 'Accès refusé' });
2c. }
L'identité vient du jeton vérifié (req.utilisateur.sub), jamais d'un paramètre modifiable par le client. Une variante, plus robuste encore, consiste à créer une route GET /api/moi/exercices qui n'accepte aucun identifiant et lit directement req.utilisateur.sub.
C1.3. a) 401 : pas authentifié. b) 403 : authentifié, mais pas autorisé pour cette ressource. c) 401 : un jeton expiré n'authentifie plus ; l'application doit en obtenir un nouveau avec son jeton de rafraîchissement.
C1.4. 1 806 000 900 − 1 806 000 000 = 900 secondes, soit 15 minutes. Le jeton d'accès accompagne chaque appel à l'API : il circule beaucoup et peut être intercepté ou lu sur un appareil compromis. Une durée courte limite le temps pendant lequel un jeton volé est utilisable. Le jeton de rafraîchissement, lui, n'est envoyé qu'au serveur d'authentification pour obtenir un nouveau jeton d'accès : il circule peu, peut être stocké de façon protégée sur le téléphone et révoqué côté serveur (perte du téléphone, changement de mot de passe). Ce couple évite de redemander le mot de passe au patient toutes les quinze minutes.
C2.1.
- Après la ligne 5 (ajout, 5a) :
* @param string $adresseIP l'adresse IP du poste à l'origine de l'accès - Ligne 7 (modifiée) :
public function journaliserAcces(int $idCompte, int $idPatient, string $action, string $adresseIP): void { - Lignes 8 et 9 (modifiées) :
8. $sql = "INSERT INTO JournalAcces (idCompte, idPatient, action, dateHeure, adresseIP)
9. VALUES (:compte, :patient, :action, NOW(), :ip)";
- Ligne 12 (modifiée) :
':action' => $action, ':ip' => $adresseIP]);
À l'appel : journaliserAcces($_SESSION['idCompte'], $idPatient, 'consultation', $_SERVER['REMOTE_ADDR']);. La requête reste préparée, le paramètre est documenté, et la trace contient l'auteur individuellement identifié, l'horodatage, la nature de l'opération, la référence des données concernées et l'équipement.
C2.2. La CNIL recommande d'enregistrer la référence des données concernées, pas les données elles-mêmes, et d'éviter de les dupliquer dans les journaux. Copier le bilan dans le journal créerait une seconde copie de données de santé, conservée plus longtemps et souvent moins protégée que la base : la surface d'exposition augmente sans aucun bénéfice, puisque idPatient suffit pour savoir quel dossier a été consulté. C'est aussi contraire au principe de minimisation (article 5 du RGPD).
C2.3.
SELECT c.login, COUNT(DISTINCT j.idPatient) AS nbDossiers
FROM JournalAcces j
JOIN Compte c ON c.id = j.idCompte
JOIN Patient p ON p.id = j.idPatient
WHERE j.action = 'consultation'
AND DATE(j.dateHeure) = '2027-03-08'
AND p.idCentre <> c.idCentre
GROUP BY c.id, c.login
HAVING COUNT(DISTINCT j.idPatient) > 3;
COUNT(DISTINCT j.idPatient) compte des dossiers différents : un dossier ouvert deux fois ne compte qu'une fois. Un COUNT(*) compterait des consultations, ce qui ne répond pas à la question. La condition sur le nombre va dans le HAVING, après le regroupement.
C2.4. Les deux lignes ne s'interprètent pas de la même façon. dsimon est médecin-chef : il supervise plusieurs centres et la règle d'accès l'autorise à consulter tous les dossiers ; ses quatre consultations peuvent être légitimes, à vérifier avec son activité du jour. apetit est kinésithérapeute à Rennes et a consulté quatre dossiers de patients d'autres centres : c'est légitime seulement s'il est leur référent. Sinon, soit le contrôle d'accès de RééduPlan est défaillant (il n'aurait pas dû pouvoir les ouvrir), soit son compte est utilisé par quelqu'un d'autre. Il faut croiser avec Patient.idReferent, interroger le praticien et, si l'accès était illégitime, traiter l'événement comme une violation de données de santé. La requête affinée exclurait les médecins-chefs et les patients dont le compte est le référent.
C2.5. La CNIL recommande une durée comprise entre six mois et un an dans le cas général (délibération n° 2021-122). Mesures d'intégrité, deux parmi : stocker les journaux sur un système séparé, accessible en écriture seule par l'application et sans possibilité d'écrasement ; horodater et signer les journaux dès leur création ; réserver la lecture à quelques personnes habilitées et alerter en cas d'accès en modification ; sauvegarder les journaux.
Tu avais trouvé ?