Sujet officiel
Cas NDV — Session 2026, U7/E7 (SLAM), durée 4h, coef 4, sur 80 points (Dossier A : 42 pts, Dossier B : 38 pts). 📄 Télécharger le sujet officiel NDV (PDF, réseaucerta.org)
Lockbay n’héberge pas le sujet : ce corrigé est une production originale Lockbay qui répond aux questions du sujet officiel. Sujet © Éducation nationale.
Le cas NDV (agence digitale) se joue en deux dossiers : sécuriser l’intranet de l’agence (logs, droits, données personnelles) puis reprendre et sécuriser le site d’un client (Architect.60 : injection SQL, IDOR, hachage). Voici un corrigé question par question.
Dossier A — Sécuriser l'intranet NDV 42 pts
Mission A1 — Journaux d’activité du SGBD
Compléter print_logs() pour filtrer aussi par nature
Le formulaire envoie type_de_logs (CONNEXION, REQUETE…). On ajoute une condition sur log_nature, sur le modèle du filtre date déjà en place — et on garde une requête préparée :
// après le bloc qui récupère $date (vers la ligne 16)
$nature = null;
if (isset($_POST['type_de_logs']) && $_POST['type_de_logs'] != '') {
$nature = $_POST['type_de_logs'];
}
// requête modifiée (ligne 17)
$query = "SELECT * FROM t_logs
WHERE (:date IS NULL OR DATE(log_date) = :date)
AND (:nature IS NULL OR log_nature = :nature)
ORDER BY log_date ASC";
// après le bindParam(':date', …) (vers la ligne 20)
$stmt->bindParam(':nature', $nature, PDO::PARAM_STR);Le motif (:param IS NULL OR colonne = :param) permet de ne filtrer que si la valeur est fournie — exactement la logique déjà utilisée pour la date.
Analyse et correction des droits de ndv_64784
a) Dans les journaux (doc A1a) — plusieurs anomalies prouvent un compte sur-privilégié et compromis :
- ligne 8 : connexion de
ndv_64784depuis 172.17.12.45 (et nonlocalhost), ce qui ne devrait pas être permis ; - lignes 9-10 :
CREATE USER 'test'@'%'puisGRANT ALL ON *.* TO 'test'@'%'— création d’un compte et octroi de tous les privilèges :ndv_64784peut donc administrer le serveur ; - ligne 14 :
DROP TABLE Contrat— suppression de table, bien au-delà des opérations CRUD attendues.
b) Dans les habilitations (doc A3) — ndv_64784 est déclaré sur l’hôte % (n’importe quelle IP), avec ALL PRIVILEGES et Grant = Oui (il peut déléguer ses droits). C’est l’inverse du moindre privilège.
c) Requêtes de correction (moindre privilège + connexion locale uniquement) :
-- 1. Retirer les droits excessifs et la délégation
REVOKE ALL PRIVILEGES, GRANT OPTION ON ndv.* FROM 'ndv_64784'@'%';
-- 2. Restreindre la connexion au serveur local
RENAME USER 'ndv_64784'@'%' TO 'ndv_64784'@'localhost';
-- 3. N'accorder que les opérations nécessaires au pilote applicatif
GRANT SELECT, INSERT, UPDATE, DELETE ON ndv.* TO 'ndv_64784'@'localhost';Mission A2 — Protéger les données personnelles
a) Une donnée à caractère personnel (CNIL/RGPD) est toute information se rapportant à une personne physique identifiée ou identifiable (nom, adresse de courriel, téléphone, IP…). Une donnée sensible relève des catégories particulières de l’article 9 du RGPD (origine, opinions, santé, orientation sexuelle, données biométriques/génétiques…), soumises à protection renforcée.
b) Dans les tables de l’intranet : les noms et adresses de courriel des contacts sont des données personnelles ; aucune donnée sensible au sens strict n’apparaît dans le schéma fourni.
Le chiffrement des données personnelles relève d’une recommandation / bonne pratique : l’article 32 du RGPD impose des « mesures techniques appropriées » et cite le chiffrement comme exemple, sans l’imposer systématiquement. C’est l’analyse de risque qui le rend nécessaire ici.
Scénario impliquant un acteur interne : un administrateur de base de données (ou un développeur) disposant d’un accès légitime à la base pourrait lire ou exfiltrer les adresses de courriel en clair. Comme la clé de chiffrement est stockée hors de la base (variable d’environnement), le chiffrement protège même contre cet accès direct au SGBD.
Adapter la colonne puis chiffrer l’existant avec la clé fournie :
-- Adapter le type pour stocker le binaire chiffré
ALTER TABLE Contact MODIFY courriel VARBINARY(200);
-- Chiffrer les adresses des contacts externes déjà stockées
UPDATE Contact SET courriel = AES_ENCRYPT(courriel, 'lhh4&abt2!');Dans getContact(), déchiffrer à l’affichage avec la clé passée par l’environnement (et toujours en requête préparée) :
SELECT id, nom, AES_DECRYPT(courriel, :cle) AS courriel
FROM Contact
WHERE id = :id;$stmt->bindParam(':cle', $_ENV['AES_key'], PDO::PARAM_STR);Mission A3 — Organisation « miroir » (schéma)
Il faut pouvoir associer, par contrat, un rôle (chef de projet, DPO, responsable sécurité API…) à deux personnes : une côté NDV, une côté client. On ajoute donc :
- une table
Role(id_role,libelle) listant les rôles génériques ; - une table d’association
AffectationreliantContrat,Role, et deux personnes (id_personne_ndv,id_personne_client) — une même personne pouvant occuper plusieurs rôles.
Ainsi un échange est rattaché à un couple (rôle, contrat) et reste confidentiel entre les deux interlocuteurs miroir.
Dossier B — Reprise du site Architect.60 38 pts
Mission B1 — Injection SQL
a) Requêtes réellement exécutées :
payload1=sbegard' OR 1=1 #→… WHERE username='sbegard' OR 1=1 #'…: la conditionOR 1=1est toujours vraie et le#commente le reste (le contrôle du mot de passe).payload2=sbegard' AND 1=0 UNION SELECT null, username, password, null, null FROM T_Users #→AND 1=0annule la première requête et l’UNION renvoie lesusername/passworddeT_Users.
b) Affichages : payload1 connecte l’attaquant (premier utilisateur trouvé) sans connaître de mot de passe ; payload2 affiche les identifiants et mots de passe de T_Users.
c) L’utilisateur sbegard n’a pas besoin d’exister : OR 1=1 (payload1) et AND 1=0 (payload2) rendent la condition sur username sans effet.
a) Pour vider la table, une charge à requêtes empilées comme sbegard'; DELETE FROM T_Documents; # (ou un DROP/TRUNCATE) si l’empilement est autorisé.
b) Le nom de la table a pu être connu via un message d’erreur SQL trop verbeux renvoyé au navigateur (il expose la structure de la base).
a) L’injection est possible parce que la requête est construite par concaténation directe de l’entrée utilisateur.
b) On la corrige avec une requête préparée PDO, qui sépare le code SQL des données :
$query = "SELECT * FROM T_Users WHERE username = :user AND password = :mdp";
$stmt = $pdo->prepare($query);
$stmt->bindParam(':user', $username, PDO::PARAM_STR);
$stmt->bindParam(':mdp', $hash, PDO::PARAM_STR);
$stmt->execute();Mission B2 — Modification d’un document d’autrui (IDOR)
a) Faille OWASP A01:2021 – Broken Access Control (variante IDOR : Insecure Direct Object Reference) : changer doc_id dans l’URL permet de modifier le document d’un autre client, car le code ne vérifie pas la propriété de la ressource.
b) Correction dans espace_client.php : vérifier que le document appartient à l’utilisateur connecté avant toute modification, par exemple en contrôlant que doc_id figure bien dans $lesDocuments (les documents de l’utilisateur), ou côté SQL :
UPDATE T_Documents
SET nom = :nom, description = :desc
WHERE doc_id = :doc_id AND client_id = :idClientConnecte;Mission B3 — Hachage des mots de passe
a) L’algorithme utilisé est un hachage rapide et sans sel (type MD5/SHA-1) : il est vulnérable aux rainbow tables et au brute-force accéléré par GPU, ce qui a permis à l’attaquant de retrouver/modifier les mots de passe.
b) Faille OWASP A02:2021 – Cryptographic Failures.
a) Dans traitement_inscription.php, hacher avec Argon2id :
$hash = password_hash($motDePasse, PASSWORD_ARGON2ID);
// puis stocker $hash en baseb) Dans espace_client.php, vérifier avec password_verify() (qui gère sel et paramètres automatiquement) :
if (password_verify($motDePasseSaisi, $hashEnBase)) {
// authentification réussie
} Points de barème (rappel)
| Dossier | Attendu | Points |
|---|---|---|
| A1 | Filtre logs + analyse/correction des droits (REVOKE/RENAME/GRANT) | — |
| A2 | Données perso/sensibles, AES_ENCRYPT/DECRYPT, scénario interne | — |
| A3 | Évolution du schéma (organisation miroir) | — |
| B1 | Décodage des injections, contre-mesure (requête préparée) | — |
| B2 | IDOR (A01) + contrôle de propriété | — |
| B3 | Hachage faible → Argon2 (password_hash/verify) | — |
| Total | Dossier A 42 + Dossier B 38 | 80 |