Sujet officiel
Cas Lama Zoo — Session 2023, U6/E6 (SLAM), durée 4h, coef 4, sur 80 points (3 dossiers : A 30 pts, B 20 pts, C 30 pts). 📄 Télécharger le sujet officiel Lama Zoo (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 parc zoologique Lama Zoo, victime d’un rançongiciel en 2019, modernise sa sécurité. Trois dossiers : refondre le système d’habilitation des applications métier (rôles SGBD, triggers, transactions), sécuriser le recueil des avis (XSS / CodeIgniter) et sécuriser le parrainage d’animaux (hameçonnage, CSRF, cloisonnement des données). Corrigé question par question.
Dossier A — Refonte du système d'habilitation 30 pts
Mission — Analyse de l’incident et conception des habilitations
a) Une attaque par force brute teste automatiquement un grand nombre de combinaisons (souvent issues de dictionnaires de mots de passe courants) jusqu’à trouver le bon. Elle a réussi contre M. Breto car son mot de passe 1234Travail est faible et prévisible (suite « 1234 » + mot du dictionnaire) : il figure dans les listes les plus courantes, ce qui réduit drastiquement le nombre d’essais. Aucune temporisation ni verrouillage après échecs ne ralentissait l’attaquant.
b) Deux moyens : organiser des sessions de formation / ateliers (mots de passe robustes, verrouillage de session) ; diffuser des supports de rappel (affiches, charte informatique, courriels de prévention) et mener des campagnes de phishing simulé.
a) Non-traçabilité : sans authentification individuelle, l’application ne peut pas attribuer les actions à un utilisateur précis. En cas d’opération frauduleuse, il est impossible d’identifier l’auteur : la responsabilité ne peut être établie, les journaux sont inexploitables.
b) Perte de confidentialité : toute personne accédant à un poste (session restée ouverte, intrus) accède librement à l’ensemble des données, sans contrôle des droits. Les données sont exposées à des personnes non autorisées, sans restriction par profil.
Intérêt des rôles applicatifs au niveau du pilote de connexion
Chaque rôle correspond à un compte SGBD distinct doté de droits précis (SELECT, INSERT… limités à certaines tables/vues). En connectant l’application à la base avec le compte du rôle de l’utilisateur, le SGBD applique le moindre privilège :
- l’utilisateur ne peut exécuter que les opérations autorisées par son rôle, même si une faille applicative envoyait une requête non prévue ;
- la défense est portée par le SGBD lui-même (défense en profondeur), pas seulement par le code ;
- en cas de compromission, l’impact est circonscrit aux droits de ce rôle.
a) Pourquoi ces manques sont des vulnérabilités :
- Habilitations sans durée de vie : les accès temporaires des stagiaires/saisonniers (Mme Moreau) ne s’expirent pas automatiquement → un compte oublié garde des droits indéfiniment (accès résiduel, usurpation d’un accès qui aurait dû être révoqué).
- Plusieurs rôles pour une même application : viole la règle « un rôle unique par application » → cumul de droits non maîtrisé, élévation de privilèges implicite, audit impossible.
b) Modifications de la base — sur la table EstHabilite :
- Ajouter des dates de validité (date de début et de fin) pour borner la durée de vie d’une habilitation ;
- Restreindre la clé primaire à
(numMatriculePerso, idAppli)afin qu’un même personnel ne puisse posséder qu’un seul rôle par application (au lieu de la clé actuelle incluantidRoleAppli).
EstHabilite (numMatriculePerso, idAppli, idRoleAppli, dateDebut, dateFin)
Clé primaire : numMatriculePerso, idAppli
Clé étrangère : numMatriculePerso → Personnel
Clé étrangère : idAppli, idRoleAppli → RoleApplicatifUn mécanisme (tâche planifiée ou vérification applicative) supprime ou ignore les habilitations dont dateFin est dépassée.
Déclencheur after_update_habilitation
Sur le modèle de after_delete_habilitation, mais déclenché AFTER UPDATE et utilisant old (ancien rôle) et new (nouveau rôle) :
CREATE TRIGGER `after_update_habilitation`
AFTER UPDATE ON `EstHabilite`
FOR EACH ROW
BEGIN
INSERT INTO HistoHabilitation
VALUES (NOW(), old.numMatriculePerso, old.idAppli,
CONCAT('Modification du rôle ', old.idRoleAppli,
' à ', new.idRoleAppli));
ENDa) Intérêt des transactions : la suppression d’un rôle réalise deux opérations indissociables (compte SGBD + ligne RoleApplicatif). La transaction garantit l’atomicité : soit les deux réussissent (commit), soit tout est annulé (rollback) en cas d’erreur. On évite un état incohérent (compte SGBD sans enregistrement associé, ou l’inverse) qui serait une faille de gestion des accès.
b) Code de deleteRole() — symétrique de createRole() : on supprime d’abord l’enregistrement de RoleApplicatif, puis le compte utilisateur du SGBD, le tout dans la transaction.
$this->monPdo->beginTransaction(); // début de la transaction
$req = $this->monPdo->prepare(
'DELETE FROM RoleApplicatif WHERE idAppli = :idAppli AND idRoleAppli = :idRole ;');
$req->bindParam(':idAppli', $idAppli, PDO::PARAM_STR);
$req->bindParam(':idRole', $id, PDO::PARAM_STR);
$resultat = $req->execute();
$req = $this->monPdo->prepare('DROP USER :idUser ;');
$req->bindParam(':idUser', $id, PDO::PARAM_STR);
$resultat = $resultat + $req->execute();
$this->monPdo->commit(); // fin de la transaction par un commit Dossier B — Sécurisation du recueil des avis 20 pts
Mission — Corriger une faille XSS sur les commentaires (CodeIgniter)
a) Rôle de la méthode before de AuthGuard : c’est un filtre exécuté avant le contrôleur. Il vérifie que la session contient isLoggedIn ; si l’utilisateur n’est pas authentifié, il interrompt le traitement et le redirige vers /connexion. Il protège donc les routes auxquelles le filtre authGuard est associé.
b) Authentification requise pour insérer un commentaire ? Oui. Les routes /ateliers/commentform/(:num) et /ateliers/commentstore portent toutes deux le filtre ['filter' => 'authGuard'] (document B3), et commentStore() ne s’exécute que if ($session->get('isLoggedIn')) (document B1, ligne 14). L’attaquant doit donc être authentifié (disposer d’un compte) pour publier un avis malveillant.
a) Pourquoi une XSS stockée : le JavaScript injecté est enregistré en base (via EvaluerModel->save(), document B1) puis réaffiché à chaque consultation de la page des avis (/ateliers/comments/5). La charge persiste côté serveur et s’exécute à chaque affichage — définition d’une XSS stockée (vs reflétée, dont la charge transite par l’URL et ne touche qu’une victime ciblée à la fois).
b) Pourquoi elle touche plus de visiteurs : la charge stockée est restituée à tout visiteur consultant la page → chaque internaute exécute le script, sans action de sa part, potentiellement des milliers de victimes. La XSS reflétée exige au contraire d’inciter chaque victime à cliquer un lien piégé : diffusion limitée et ciblée.
a) Filtrer les données issues de la base avant affichage : on ne doit jamais faire confiance aux données stockées (insérées par une voie non contrôlée, une version antérieure non sécurisée ou par injection). Échapper à l’affichage garantit qu’un contenu malveillant déjà en base ne s’exécutera pas — protection principale contre la XSS.
b) Filtrer les données issues du formulaire avant écriture : évite de stocker une charge malveillante (assainissement à la source), limite la propagation à d’autres canaux (API, exports) et protège les traitements qui ne ré-échapperaient pas à l’affichage.
a) Méthode commentStore (AteliersController, document B1) — échapper le commentaire avant l’enregistrement. Ligne modifiée :
27. 'commentaireEvaluer' => esc($this->request->getVar('comment')),b) Vue coms.php (document B2) — échapper chaque commentaire à l’affichage. Ligne modifiée :
4. echo esc($com)."<br/>";La méthode esc() (document B6) transforme par exemple <br> en <br> : le code injecté est affiché comme texte inerte et n’est plus interprété comme du HTML/JavaScript.
Dossier C — Sécurisation du parrainage d'animaux 30 pts
Mission 1 — Hameçonnage et infractions
Cinq indices d’hameçonnage
(document C1) :
- Adresse d’expéditeur incohérente :
[email protected]hors du domaine officiel@lamazoo.com. - Fautes d’orthographe : « nous vous offront », « 4 entrées gratuite », « Léquipe zoo ».
- Offre trop alléchante / appât : 4 entrées gratuites « pour vous remercier » → incitation à cliquer.
- Lien trompeur : le texte affiché (« cestla fete enfamille ») masque une URL pointant vers une action sensible
https://lamazoo.com/profil/supprcmptconfirm(suppression de compte), sans rapport avec l’offre. - Caractère générique : formule impersonnelle (« cher Parrain »), signature vague, absence de coordonnées vérifiables.
Deux infractions retenues
contre l’auteur :
- Accès et maintien frauduleux dans un système de traitement automatisé de données (STAD) — art. 323-1 du Code pénal (et l’atteinte au fonctionnement / suppression de données, art. 323-2 et 323-3, du fait de la suppression du compte).
- Escroquerie / collecte frauduleuse de données par usurpation d’identité : usurpation de l’identité de Lama Zoo pour tromper la victime — escroquerie (art. 313-1) et usurpation d’identité (art. 226-4-1), ainsi que la collecte déloyale de données personnelles (art. 226-18).
Mission 2 — Protection contre le CSRF (CodeIgniter)
a) Éléments stockés côté client / serveur pour les deux patrons (documents C2) :
| Patron de conception | Côté serveur | Côté client |
|---|---|---|
| Synchronizer Token | Le jeton CSRF est stocké en variable de session sur le serveur | Le jeton est inséré dans un champ caché du formulaire (ou paramètre URL) |
| Double Submit (cookie) | Rien n’est stocké côté serveur (le serveur compare seulement les deux valeurs reçues) | Le jeton est envoyé deux fois : dans un cookie et dans le corps de la requête (champ caché) |
b) Activer le Synchronizer Token :
- Dans la classe
Security(document B4) : passer la propriété$csrfProtectionde'cookie'à'session':public $csrfProtection = 'session'; - Dans la classe
Filters: activer le filtrecsrfavant chaque requête, en l’ajoutant au tableau$globals['before']:'before' => [ 'honeypot', 'invalidchars', 'csrf', ], - Dans les vues, les développeurs doivent insérer dans chaque formulaire à protéger l’appel
<?= csrf_field() ?>(document C3), qui génère le champ caché contenant le jeton.
a) Pourquoi les routes API ne répondent plus : la protection CSRF est activée globalement (filtre csrf dans before). Or les routes /ws/… sont des services web appelés sans formulaire HTML : elles ne transmettent aucun jeton CSRF. Le filtre, ne trouvant pas le jeton attendu, rejette toutes les requêtes. Ces routes étant publiques et en lecture seule, un scénario CSRF n’a pas de sens : il faut les exempter.
b) Whitelist dans Filters — utiliser la clé except du filtre csrf avec une expression régulière couvrant toutes les routes /ws/… (document C3) :
public $filters = [
'csrf' => ['except' => ['ws/*']],
];'ws/*' exclut les trois routes API du document B3 (ws/animaux, ws/hebergements, ws/ateliers) du filtre CSRF.
Mission 3 — Cloisonner l’accès aux données par vue SQL
Requêtes demandées
: retirer les droits trop larges de site_public, créer une vue n’exposant que les champs de la fiche publique (document C6 : nom, espèce, année de naissance, poids, commentaire), puis n’accorder qu’un SELECT sur cette vue.
-- 1. Retirer les droits actuels (trop larges : ALL sur la table animal)
REVOKE ALL ON BdAnimaux.animal FROM 'site_public';
-- 2. Créer la vue n'exposant que les informations publiques
CREATE VIEW BdAnimaux.vueAnimal AS
SELECT a.nomAnimal,
e.nomCommunEspece,
YEAR(a.dateNaissAnimal) AS anneeNaissance,
a.poidsAnimal,
a.commentaire,
a.photoAnimal
FROM Animal a
JOIN Espece e ON a.numEspece = e.numEspece;
-- 3. N'accorder qu'un droit de lecture sur la vue
GRANT SELECT ON BdAnimaux.vueAnimal TO 'site_public';La vue masque les champs sensibles (paysOriAnimal, numZone, filiation…) qui faciliteraient un vol d’animal, et le compte n’a plus d’accès direct aux tables ni de droit d’écriture.
Tableau d’honneur pseudonymisé
Le tableau initial divulgue des données personnelles (nom complet). Pour respecter la minimisation (RGPD) tout en laissant chaque parrain se reconnaître, on pseudonymise : on affiche par exemple prénom + initiale du nom (ou un pseudonyme choisi par le parrain), jamais d’identifiant direct (nom complet, e-mail, ville).
| Rang | Parrain | Animal parrainé | Montant |
|---|---|---|---|
| 1 | Edwin H. | Serge (lama) | … € |
| 2 | Lucie P. | … | … € |
| 3 | A. M*** | … | … € |
Points de barème (rappel)
| Dossier | Attendu | Points |
|---|---|---|
| A | Force brute & sensibilisation, traçabilité/confidentialité, rôles SGBD (moindre privilège), durée de vie & unicité des habilitations, trigger UPDATE, transactions & deleteRole() | 30 |
| B | Filtre AuthGuard, XSS stockée vs reflétée, échappement entrée/sortie, correction commentStore & coms.php avec esc() | 20 |
| C | Indices d’hameçonnage, infractions pénales, CSRF (synchronizer token, client/serveur, activation), whitelist API, vue SQL & moindre privilège, pseudonymisation | 30 |
| Total | 80 |