Injection SQL, requêtes préparées PDO, hachage Bcrypt, OTP, API REST Java, triggers MySQL, signature électronique eIDAS et contraintes métier aéronautiques.
Le sujet Denas est un cas très complet de BTS SIO SLAM : il ne suffit pas d’écrire du code, il faut sécuriser l’authentification, justifier les protections contre les injections SQL, contrôler les données d’une API REST et relier les choix techniques aux exigences juridiques du secteur aéronautique.
Denas fabrique des pièces aéronautiques coûteuses et réglementées. L’application doit donc assurer la confidentialité des comptes clients, la fiabilité des demandes, la conformité des échanges contractuels et la traçabilité des actions sensibles.
Une bonne réponse ne dit pas seulement “sécuriser la connexion”. Elle montre pourquoi la concaténation SQL est dangereuse, pourquoi un mot de passe ne doit jamais être chiffré de manière réversible, et comment l’OTP, les logs et les triggers réduisent concrètement le risque.
' OR '1'='1.password_hash avec Bcrypt pour stocker le mot de passe.“La requête actuelle concatène directement le login dans le SQL. Un attaquant peut saisir une expression qui modifie la condition WHERE. La correction consiste à utiliser une requête préparée avec un paramètre lié afin que la saisie soit traitée comme une donnée et jamais comme du code SQL.”
“Un mot de passe ne doit pas être chiffré avec AES, car ce chiffrement est réversible. Il doit être haché avec Bcrypt et un sel aléatoire. Même si la base est volée, l’attaquant ne peut pas retrouver directement les mots de passe originaux.”
“La signature électronique qualifiée permet d’engager juridiquement le client. Elle s’appuie sur un certificat qualifié délivré par une autorité de certification reconnue et possède une valeur juridique équivalente à la signature manuscrite dans le cadre du règlement eIDAS.”
AES est réversible. Pour un mot de passe, la bonne réponse est le hachage irréversible avec Bcrypt, pas le chiffrement.
Une requête préparée doit être clairement construite avec un paramètre lié. Le correcteur attend une séparation explicite entre la requête et la donnée utilisateur.
201 signifie que la ressource est créée. 400 signifie que la requête est invalide. Une réponse sans justification technique perd de la valeur.
Dans un contexte contractuel professionnel, la référence au règlement eIDAS et à la signature qualifiée renforce nettement la réponse.
Le cas Denas illustre le développement sécurisé d'une application web pour un fabricant de pièces aéronautiques servant 300 clients professionnels (compagnies aériennes, ateliers de maintenance agréés). Chaque pièce détachée (aileron, train d'atterrissage, composant électronique) coûte 5000-500000€ et nécessite une traçabilité totale réglementaire. L'épreuve SLAM teste votre capacité à sécuriser l'authentification contre injections SQL avec requêtes préparées PDO, implémenter un double facteur OTP, développer une API REST Java avec validation robuste, automatiser le blocage clients abusifs par triggers MySQL, garantir la conformité juridique avec signature électronique qualifiée eIDAS, et modéliser les contraintes métier complexes (compatibilité pièce-avion).
Piège mortel : Utiliser AES_ENCRYPT() MySQL ou openssl_encrypt() PHP pour "protéger" les mots de passe car "c'est chiffré donc sécurisé".
Pourquoi c'est catastrophique : Le chiffrement symétrique AES est RÉVERSIBLE avec la clé. Si un attaquant vole la base de données ET trouve la clé de chiffrement dans votre code (probabilité 80-90% car souvent hardcodée), il déchiffre TOUS les 300 mots de passe clients en quelques minutes. Les clients réutilisant le même mot de passe sur d'autres sites (60-70% selon études) se font pirater leurs comptes bancaires, emails professionnels. Votre entreprise fait faillite sous les poursuites judiciaires.
Solution obligatoire : Utilisez le HACHAGE irréversible avec Bcrypt. password_hash($mdp, PASSWORD_BCRYPT) génère un hash impossible à inverser même avec la clé. Ajoutez un sel aléatoire automatique (Bcrypt le gère) empêchant les rainbow tables. Le correcteur mettra 0/20 si vous proposez du chiffrement pour les mots de passe.
Code actuel DANGEREUX (ligne 6) :
$req = "SELECT * FROM client WHERE login = '" . $login . "'";
$res = $connex->query($req);
Attaque par injection SQL : Un pirate saisit ' OR '1'='1 comme login. La requête devient WHERE login = '' OR '1'='1' qui est TOUJOURS vraie. Le pirate récupère tous les 300 clients avec leurs données sensibles sans connaître aucun login légitime.
Code sécurisé OBLIGATOIRE avec requêtes préparées :
// Ligne 6 : requête avec paramètre ? au lieu de concaténation
$req = "SELECT id, nom, login, pass, courriel FROM client WHERE login = ?";
// Ligne 7 : préparation de la requête
$prep = $connex->prepare($req);
// Ligne 8 : liaison du paramètre avec type STRING explicite
$prep->bindValue(1, $login, PDO::PARAM_STR);
// Ligne 9 : exécution sécurisée
$prep->execute();
// Ligne 10 : récupération résultat
$enreg = $prep->fetch(PDO::FETCH_OBJ);
Séparation structure vs données : Le SGBD MySQL reçoit d'abord la STRUCTURE de la requête (WHERE login = ?) qu'il compile et optimise. Ensuite seulement il reçoit les DONNÉES (valeur du $login) qu'il traite comme de simples chaînes de caractères, JAMAIS comme du code SQL exécutable.
Tentative d'injection neutralisée : Si le pirate saisit ' OR '1'='1, le SGBD ne l'interprète PAS comme du SQL mais comme la valeur littérale recherchée. La requête cherche un client dont le login vaut exactement la chaîne ' OR '1'='1 (23 caractères incluant les apostrophes), évidemment inexistant. L'attaque échoue à 100%.
Gain sécurité mesuré : Les requêtes préparées bloquent 100% des tentatives d'injection SQL, principale cause de 35-45% des piratages d'applications web selon l'OWASP. Coût de mise en œuvre : 3 lignes de code supplémentaires par requête. Temps développement : +5 minutes. Bénéfice : vous évitez une fuite de données coûtant 3-5 millions d'euros en amendes RGPD + perte clientèle.
Étape 1 - Récupération code OTP formulaire (après ligne 10) :
$otpForm = filter_input(INPUT_POST, 'otp', FILTER_SANITIZE_STRING);
Étape 2 - Vérification OTP après validation mot de passe (lignes 20-24) :
// Génère le code OTP attendu à cet instant
$uneDateTime = new DateTime();
$unOtp = new OTP($client);
$codeAttendu = $unOtp->getCode($uneDateTime);
// Compare avec code saisi par utilisateur
if ($otpForm != $codeAttendu) {
$erreur = true;
syslog(LOG_WARNING, "Erreur connexion client " . $loginForm .
", cause : Erreur OTP");
}
Étape 3 - Journalisation connexion réussie (après ligne 20 dans le if password_verify) :
syslog(LOG_INFO, "Connexion client " . $client->getId());
Étape 4 - Journalisation erreur login/mdp (après ligne 15 dans le if $client == null) :
syslog(LOG_WARNING, "Erreur connexion client " . $loginForm .
", cause : Erreur login/mot de passe");
Réduction attaques brute force : Un code OTP change toutes les 30 secondes. Un pirate testant 1000 mots de passe par seconde a 1000/1000000 = 0,1% de chance de deviner le bon couple mdp+OTP dans la fenêtre de 30 secondes. Avec 300 clients, probabilité de compromission d'un compte passe de 85% (mdp seul) à 0,03% (mdp+OTP), soit réduction 99,96%.
Détection anomalies avec logs : Les logs syslog permettent d'identifier : 500 tentatives échouées sur le compte "airfrance" en 10 minutes = attaque brute force en cours, connexions réussies à 3h du matin alors que le client se connecte habituellement 9h-18h = probable compromission compte. Configurez une alerte automatique si >10 échecs authentification en 5 minutes pour bloquer l'IP attaquante.
Implémentation DemandeRest.java :
@POST
public Response createDemande(Demande laDemandeJson) {
boolean ret = DemandeService.create(laDemandeJson);
if (ret == true) {
// Création réussie : retourne code HTTP 201 CREATED
return Response.status(Status.CREATED).build();
} else {
// Échec validation ou contraintes : retourne 400 BAD_REQUEST
return Response.status(Status.BAD_REQUEST).build();
}
}
Validation robuste verifDonneesCreate :
// Vérifie typeEchange vaut EXACTEMENT STANDARD, SILVER ou GOLD
if (Pattern.matches("STANDARD|SILVER|GOLD", laDem.getTypeEchange()) == false) {
res = false; // Refuse BRONZE, standard, Standard, etc.
}
// Contrôle date pas dans le futur (demande datée demain = fraude)
if (laDem.getDateDemande().after(new Date())) {
res = false;
}
Test création valide :
// Crée demande avec données conformes
Demande dem4 = new Demande(null, "SILVER", "INTERNE",
new Date(), null, unClient, unTypePiece);
// Vérifie que create() retourne true
assertTrue(DemandeService.create(dem4));
// Garantit que modifications futures ne cassent pas la création
Importance tests automatisés : Dans 6 mois, un développeur modifiera DemandeService.create() pour ajouter une fonctionnalité. Sans tests, probabilité 40-60% d'introduire une régression cassant la création de demandes existantes. Avec tests lancés automatiquement à chaque commit, détection immédiate de la régression avant mise en production.
SELECT c.id, c.nom, COUNT(p.numSerie) AS nbPieces
FROM Client c
JOIN Demande d ON c.id = d.idClient
JOIN Piece p ON d.id = p.idDemande
WHERE d.dateConfirmation IS NULL
AND d.dateDemande < NOW() - INTERVAL 24 HOUR
GROUP BY c.id, c.nom;
Explication logique : Joint Client → Demande → Piece pour compter les pièces bloquées. Filtre sur dateConfirmation IS NULL (demandes jamais confirmées) ET dateDemande < NOW() - 24 heures (anciennes de plus d'un jour). Groupe par client et compte le nombre de pièces immobilisées inutilement.
CREATE OR REPLACE TRIGGER verif_abus
BEFORE INSERT ON Demande
FOR EACH ROW
BEGIN
DECLARE nbAbus int;
-- Calcule nombre d'abus du client via fonction nb_abus()
SELECT nb_abus(NEW.idClient) INTO nbAbus;
-- Si >= 2 abus, bloque le client et annule l'insertion
IF nbAbus >= 2 THEN
UPDATE Client SET estBloque = true WHERE id = NEW.idClient;
CALL EXIT; -- Annule INSERT de la nouvelle demande
END IF;
END;
Fonctionnement automatique : Avant chaque INSERT de demande, MySQL exécute automatiquement ce trigger. Il interroge la fonction nb_abus() comptant les demandes abusives du client. Si ≥2, passe estBloque = true et annule l'insertion avec EXIT. Le client ne peut plus créer de demandes immédiatement, sans intervention manuelle administrateur.
Exigence 1 - Confidentialité totale : Chiffrez documents avec la clé publique RSA du client (algorithme asymétrique). Seul le client possédant la clé privée correspondante peut déchiffrer. Un pirate interceptant le document chiffré ne peut pas le lire.
Exigence 2 - Authenticité et intégrité : Signez numériquement documents avec la clé privée de Denas. Le client vérifie la signature avec la clé publique Denas. Si la signature est valide, garantie que le document provient bien de Denas (authenticité) et n'a pas été modifié (intégrité). Toute modification invalide la signature.
Exigence 3 - Preuve de réception : Utilisez un service d'envoi recommandé électronique (ARE) certifiant date et heure de réception avec valeur légale. Alternative : accusé de lecture email avec horodatage qualifié. Constitue une preuve opposable en justice.
Exigence 4 - Signature engageante : Le client signe électroniquement le contrat avec un certificat électronique qualifié délivré par une Autorité de Certification reconnue (ChamberSign, Certinomis). Selon le règlement eIDAS européen, la signature électronique qualifiée a la MÊME valeur juridique qu'une signature manuscrite. Le contrat est juridiquement opposable.
Exemple mauvais : "Je chiffre le mot de passe avec AES" (0 point, erreur conceptuelle grave). Exemple excellent : "J'utilise password_hash($mdp, PASSWORD_BCRYPT) qui génère un hash SHA-256 irréversible avec sel aléatoire automatique. Impossible de retrouver le mdp original même en volant la BDD. Le chiffrement AES est réversible donc inadapté aux mots de passe" (8-10 points).
Le correcteur teste votre code. Une erreur de syntaxe ($prep->bindValue(1 $login) au lieu de (1, $login)) fait planter le code = 0 point même si la logique est bonne. Relisez TOUJOURS votre code, vérifiez les virgules, points-virgules, accolades.
Exemple mauvais : "J'utilise un trigger" (2 points). Exemple excellent : "J'utilise un trigger BEFORE INSERT qui s'exécute automatiquement avant chaque création de demande, interroge nb_abus() pour compter les abus du client, bloque si ≥2. Avantages : automatisation 100% sans intervention humaine, temps réponse <1ms, impossible de contourner car contrôle au niveau SGBD" (8-10 points).
Mentionnez le règlement eIDAS pour la signature électronique qualifiée, le RGPD pour la protection des données clients, l'OWASP Top 10 pour les injections SQL. Ces références rapportent 3-5 points bonus et prouvent votre culture professionnelle.
Créez un jeu de données de test : 3 clients avec 0, 1 et 3 abus. Exécutez votre requête SQL, vérifiez qu'elle retourne uniquement le client avec 3 abus. Testez votre trigger : insérez une demande pour le client abusif, vérifiez que estBloque passe à true. Tests = 12-15 points supplémentaires.
Injection SQL + requêtes préparées : 20-25 points
Hachage Bcrypt + OTP + logs : 20-25 points
API REST + validation + tests : 20-25 points
Trigger blocage + requête SQL : 15-20 points
Signature électronique + modélisation : 15-20 points
Le sujet porte sur le développement sécurisé : injection SQL, authentification forte, API REST, triggers MySQL et signature électronique.
Bcrypt produit un hash irréversible avec sel. C’est adapté au stockage des mots de passe, contrairement au chiffrement réversible.
Il faut utiliser des requêtes préparées PDO avec paramètres liés au lieu de concaténer directement une saisie utilisateur dans la requête.
Le code 201 Created indique que la ressource a bien été créée. En cas de données invalides, 400 Bad Request est plus adapté.
Elle donne une valeur juridique forte au contrat signé et permet de sécuriser l’engagement du client dans le cadre du règlement eIDAS.
Retrouvez les autres sujets BTS SIO disponibles pour compléter vos révisions.