Sujet officiel
Cas Denas — Session 2022, U6/E6 (SLAM), durée 4h, coef 4, sur 80 points (4 dossiers : 12 + 26 + 24 + 18 pts). Code sujet 22SI5SLAM-NC.
📄 Télécharger le sujet officiel Denas (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 Denas (électronique aéronautique) porte sur l’application Web PartEdge : demandes de pièces de rechange transmises au PGI via une API REST. Quatre dossiers : analyse de risque, authentification (hachage, anti-injection, 2FA), validation des demandes (signature, trigger, modélisation) et API REST. Corrigé question par question.
Dossier A — Participation à l'atelier d'analyse de risque 12 pts
Critères de sécurité (DICP) pour les cas d’utilisation n°2 et 6
On évalue les quatre critères : Confidentialité, Intégrité, Disponibilité, Preuve (0 / + / ++).
| Cas | Confidentialité | Intégrité | Disponibilité | Preuve |
|---|---|---|---|---|
| 2 — Consulter le catalogue | + | ++ | ++ | 0 |
| 6 — Signer électroniquement | ++ | ++ | + | ++ |
Justification : pour le cas 2, le catalogue (prix, stocks) est sensible commercialement mais surtout il doit être exact (intégrité ++) et disponible 24/7 (++) ; aucune preuve n’est requise pour une simple consultation. Pour le cas 6, la signature engage juridiquement le client : la preuve est primordiale (++), de même que la confidentialité et l’intégrité des documents contractuels.
Impact métier des événements redoutés n°4 et 5
- ER n°4 (client valide des demandes de pièces non compatibles avec l’aéronef) : immobilisation indue de pièces retirées du stock pour une demande inexploitable, rupture de stock pour les vrais besoins, avions cloués au sol chez d’autres clients (jusqu’à 25 000 €/jour) et perte de chiffre d’affaires et d’image → gravité ++.
- ER n°5 (attaquant accède aux données du PGI via l’interface de demande) : compromission du cœur du SI (commandes, stocks, facturation, clients), fuite ou altération de données contractuelles et financières, traçabilité aéronautique faussée mettant en jeu la sécurité des aéronefs → gravité ++.
Deux mesures pour les scénarios de risques n°2 et 5
- Scénario 2 (outil d’attaque des mots de passe / brute force) :
- Hacher les mots de passe avec salage (Bcrypt) — un vol de base ne livre pas les mots de passe en clair ;
- Limiter les tentatives (verrouillage/temporisation après N échecs) et imposer une politique de mots de passe robustes + 2FA (OTP).
- Scénario 5 (corruption du PGI par injection de code dans les demandes) :
- Valider et typer toutes les données reçues par l’API REST (listes blanches, expressions régulières) avant transmission au PGI ;
- Cloisonner l’API en DMZ avec un compte de service au moindre privilège côté PGI et des requêtes paramétrées côté base.
Motivations des scénarios n°3 et 4
Ces deux scénarios relèvent de la cyber-influence concurrentielle : un client peu scrupuleux réserve des pièces qu’il ne validera pas (scénario 3) ou demande des pièces incompatibles (scénario 4) afin d’assécher le stock disponible. Comme le stock est très faible (pandémie, délais fournisseurs allongés), l’objectif est de priver les concurrents des pièces dont ils ont besoin et de provoquer l’immobilisation de leurs aéronefs (pertes pouvant atteindre 25 000 €/jour), gagnant ainsi un avantage commercial déloyal.
Dossier B — Amélioration de l'authentification 26 pts
Chiffrement (AES) ou hachage (SHA) pour les mots de passe ?
Le hachage est la solution la plus sûre. Le chiffrement symétrique (AES) est réversible : la clé de déchiffrement doit être stockée quelque part ; si elle est volée avec la base, tous les mots de passe sont retrouvés en clair. Le hachage est à sens unique (irréversible) : on ne stocke jamais le mot de passe, on compare seulement les empreintes. De plus, l’application n’a jamais besoin de relire un mot de passe, seulement de le vérifier — le hachage suffit.
Importance du salage (grain de sel) avec Bcrypt
Le sel est une valeur aléatoire et unique par utilisateur, ajoutée au mot de passe avant hachage. Il garantit que deux clients ayant le même mot de passe obtiennent des empreintes différentes, ce qui :
- rend inutilisables les tables précalculées (rainbow tables) ;
- empêche de repérer les mots de passe identiques entre comptes ;
- oblige l’attaquant à casser chaque empreinte individuellement.
Bcrypt génère et intègre automatiquement le sel dans l’empreinte produite, et applique un facteur de coût (lenteur volontaire) qui ralentit fortement les attaques par force brute.
Sécuriser readByLogin (ClientDao) contre l’injection SQL
Le code original (Document B4) concatène $login dans la requête (ligne 6) — faille d’injection directe. On remplace par une requête préparée avec un paramètre lié.
// Ligne 6 — modifiée : on remplace la valeur concaténée par un marqueur ?
$req = "SELECT id, nom, login, pass, courriel, telephone, adresse,
pays, estBloque, codePinOtp FROM client WHERE login = ?";
// Lignes 6bis/7 — insérées : préparation, liaison de la valeur, exécution
$prep = $connex->prepare($req);
$prep->bindValue(1, $login, PDO::PARAM_STR);
$prep->execute();
// Ligne 8 — modifiée : on lit le résultat sur l'objet préparé
$enreg = $prep->fetch(PDO::FETCH_OBJ);
// Ligne 16 — modifiée : fermeture du curseur de la requête préparée
$prep->closeCursor();La valeur saisie n’est jamais interprétée comme du code SQL : elle est transmise séparément de la requête. La concaténation '" . $login . "' est supprimée.
Deux autres solutions de 2FA
Outre la clé OTP physique :
- OTP par application d’authentification (TOTP type Google Authenticator / FreeOTP) ou par SMS / e-mail envoyé à un canal de confiance ;
- Authentification biométrique (empreinte digitale, reconnaissance faciale) ou clé matérielle FIDO2/U2F (type YubiKey) branchée sur le poste.
Ajout du champ OTP dans loginView.php
On insère un champ de saisie pour le code à 6 chiffres entre le mot de passe et le bouton (après la ligne 3) :
<!-- Ligne 3bis — insérée -->
<label for="otp">Code OTP :</label>
<input type="text" id="otp" name="otp" maxlength="6" pattern="[0-9]{6}" required/>Vérification OTP et journalisation dans verifLogin (LoginCtrl)
On récupère le code OTP saisi, on le compare à celui généré par la classe OTP, et on journalise chaque tentative via syslog() selon le format imposé (Documents B1 et B6). Insertions dans verifLogin :
// Ligne 10bis — insérée : récupération du code OTP saisi
$otpForm = filter_input(INPUT_POST, 'otp', FILTER_SANITIZE_NUMBER_INT);
// Lignes 19 à 24 — remplacées : mot de passe OK -> on contrôle l'OTP, puis on journalise
if (password_verify($passForm, $client->getPass())) {
$unOtp = new OTP($client);
$codeOk = $unOtp->getCode(new DateTime());
if ($otpForm == $codeOk) {
// Connexion réussie : on journalise puis on affiche l'accueil
syslog(LOG_INFO, "Connexion client " . $client->getId());
new AccueilView();
} else {
// Mot de passe valide mais OTP erroné
syslog(LOG_WARNING, "Erreur connexion client " . $loginForm . ", cause : Erreur OTP");
$erreur = true;
}
} else {
// Couple login / mot de passe invalide
syslog(LOG_WARNING, "Erreur connexion client " . $loginForm . ", cause : Erreur login/mot de passe");
$erreur = true;
}Le cas
$client == null(login inconnu, ligne 14) doit lui aussi journaliser unLOG_WARNINGavec la causeErreur login/mot de passe. On termine parcloselog();.
Dossier C — Validation des demandes client 24 pts
Condition de recevabilité de l’échange électronique devant les tribunaux
L’échange électronique a la même valeur probante que l’écrit papier à condition que l’on puisse identifier de manière fiable l’auteur du document et garantir l’intégrité du contenu (document non modifié), via une signature électronique s’appuyant sur un procédé fiable (certificat délivré par un prestataire de confiance qualifié, conformément au règlement eIDAS et au Code civil).
Solutions techniques pour les quatre exigences
| Exigence du service juridique | Solution technique |
|---|---|
| 1. Confidentialité des échanges | Chiffrement des documents (chiffrement de la communication TLS et/ou du document avec la clé publique du destinataire) |
| 2. Authenticité + non-modification | Signature électronique : empreinte (hachage) du document signée avec la clé privée de l’émetteur ; toute modification invalide la signature |
| 3. Preuve de réception par le client | Accusé de réception (envoi recommandé électronique / horodatage qualifié), idéalement contre-signé par le destinataire |
| 4. Engagement légal du client | Signature électronique du client reposant sur un certificat qualifié (eIDAS), qui l’engage juridiquement |
Requête : clients avec demandes de plus de 24 h non confirmées
Une demande est abusive si elle n’est pas confirmée (dateConfirmation IS NULL) plus de 24 h après sa création. On affiche id, nom et le nombre total de pièces demandées.
SELECT c.id, c.nom, COUNT(d.id) AS nbPieces
FROM Client c
JOIN Demande d ON d.idClient = c.id
WHERE d.dateConfirmation IS NULL
AND d.dateDemande < NOW() - INTERVAL 24 HOUR
GROUP BY c.id, c.nom;Trigger de blocage des clients abusifs
Avant l’insertion d’une demande, on appelle nb_abus(idClient). Si le client atteint 2 abus, on le bloque (estBloque = true) et on empêche la création de la demande (levée d’erreur).
CREATE OR REPLACE TRIGGER verif_client_abus
BEFORE INSERT ON Demande
FOR EACH ROW
BEGIN
DECLARE nb INT;
SET nb = nb_abus(NEW.idClient);
IF nb >= 2 THEN
UPDATE Client SET estBloque = TRUE WHERE id = NEW.idClient;
-- on empêche l'enregistrement de la nouvelle demande
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Client bloqué pour demandes abusives';
END IF;
END;Modélisation de la compatibilité type de pièce / modèle d’aéronef
Un type de pièce peut équiper plusieurs modèles d’aéronef, et un modèle accepte plusieurs types de pièce → relation plusieurs-à-plusieurs, avec un commentaire porté par chaque compatibilité. On ajoute une entité ModeleAeronef et une table d’association Compatibilite :
Schéma relationnel ajouté :
ModeleAeronef(id, libelle)
Compatibilite(idTypePiece, idModele, commentaire)
clé primaire : (idTypePiece, idModele)
clés étrangères : idTypePiece -> TypePiece(id)
idModele -> ModeleAeronef(id)En diagramme de classes : association *..* entre TypePiece et ModeleAeronef, réifiée par une classe-association Compatibilite portant l’attribut commentaire.
Implémenter la contrainte métier (sans la coder)
Lors de la saisie d’une demande, vérifier que le couple (type de pièce demandé, modèle d’aéronef concerné) existe dans la table Compatibilite. Cette contrainte peut être mise en œuvre par un déclencheur (trigger) BEFORE INSERT sur Demande qui rejette la demande si aucune ligne de compatibilité ne correspond (et/ou par un contrôle applicatif côté service avant l’appel à la base).
Dossier D — Envoi des données au PGI / API REST 18 pts
Méthode REST createDemande (POST) dans DemandeRest
On appelle DemandeService.create() ; en cas de succès on renvoie 201 CREATED, sinon 400 BAD_REQUEST (la requête n’a pas pu être réalisée).
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createDemande(DemandeJson laDemandeJson) {
boolean ret = DemandeService.create(laDemandeJson);
if (ret == true) {
return Response.status(Status.CREATED).build();
} else {
return Response.status(Status.BAD_REQUEST).build();
}
}Contrôles dans verifDonneesCreate (regex) + contrôle supplémentaire
a) Le code fourni contient des regex erronées : ^[0-9]4-[0-9]2-[0-9]2$ n’exprime pas une répétition (il faut {4}, {2}, {2}) et INTERNE|EXTERNE n’est pas ancrée (elle accepterait XINTERNEY). On corrige et on ajoute le contrôle du type d’échange :
// Date au format ISO YYYY-MM-DD (corrigé avec les quantificateurs {n})
if (Pattern.matches("^[0-9]{4}-[0-9]{2}-[0-9]{2}$", laDem.getDateDemande()) == false) {
res = false;
}
// Mode de transport : INTERNE ou EXTERNE uniquement (ancré ^...$)
if (Pattern.matches("^(INTERNE|EXTERNE)$", laDem.getModeTransport()) == false) {
res = false;
}
// ** Ajout : type d'échange STANDARD, SILVER ou GOLD uniquement
if (Pattern.matches("^(STANDARD|SILVER|GOLD)$", laDem.getTypeEchange()) == false) {
res = false;
}b) Contrôle supplémentaire sur la date : une regex valide le format mais pas la validité réelle de la date (2022-03-33 passe le motif). Il faut vérifier que la date existe vraiment (parsing strict avec SimpleDateFormat.setLenient(false) ou LocalDate.parse) et, métier, qu’elle est cohérente (par exemple non postérieure à la date du jour).
Test JUnit manquant : create avec une demande valide
Les trois tests existants couvrent les cas invalides (transport, échange, date). Il manque le cas nominal où toutes les données sont valides et où create doit renvoyer true. On ajoute une demande valide dans setUp() puis le test :
// Dans setUp() — ligne 16 : une 4e demande entièrement valide
this.dem4 = new DemandeJson(4, "2022-03-20", " ",
"GOLD", 0, "INTERNE", 44, "A320", 2, 3, null);
// Nouveau test — ligne 44 : demande valide -> create doit réussir
@Test
public void testCreateDemandeValide() {
boolean result = DemandeService.create(dem4);
assertEquals("Create avec une demande valide", true, result);
}On déclarera le champ
private DemandeJson dem4;en début de classe (à côté dedem1/dem2/dem3).
Points de barème (rappel)
| Dossier | Attendu | Points |
|---|---|---|
| A | Critères DICP (cas 2 et 6), impacts ER 4/5, mesures scénarios 2/5, motivations cyber-influence | 12 |
| B | Hachage vs chiffrement, salage Bcrypt, requête préparée anti-injection, 2FA, champ OTP, vérification OTP + journalisation syslog | 26 |
| C | Signature électronique/eIDAS, 4 exigences, requête abus, trigger blocage, modélisation compatibilité, contrainte métier | 24 |
| D | API REST POST (201/400), contrôles regex + validité date, test JUnit nominal | 18 |
| Total | 80 |