Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 912 minIntermédiaire

Journalisation et tests de sécurité

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • dire ce qu'une trace doit contenir selon la CNIL ;
  • compléter une méthode de journalisation ;
  • repérer une attaque dans un journal de serveur Web ;
  • expliquer et écrire des tests unitaires d'un contrôle d'accès.

2 exercices corrigés1 schéma

La journalisation consiste à enregistrer les actions des utilisateurs et des programmes pour détecter une attaque, en comprendre le déroulement et en établir la responsabilité ; les tests de sécurité vérifient qu'une protection fonctionne vraiment. Les sujets demandent de compléter une méthode qui écrit dans un journal, de lire un journal pour y repérer une attaque, de modéliser un système d'alertes et d'expliquer ou d'écrire des tests unitaires. Ce chapitre couvre ces quatre situations.

Ce qu'un journal doit contenir

La CNIL a publié en 2021 une recommandation sur la journalisation (délibération n° 2021-122). Elle dit ce qu'une trace doit contenir et comment la conserver ; c'est la référence attendue quand un sujet parle de « bonnes pratiques ».

1. Enregistrer2. Protéger3. Analyser4. Conserver5. Supprimerauteur individuellement identifié, horodatage, naturede l'opération, référence des données, adresse IPstockage séparé du système principal, en écriture seule,journaux horodatés et signés dès leur créationtraitement automatique qui génère des alertesentre six mois et un an dans le cas généralà la fin de la durée fixée et documentéeÀ ne jamais journaliserun mot de passe ; une copie inutile des données personnelles du traitement
Une trace utile : qui, quand, quoi, sur quelle donnée, protégée, analysée, conservée de six mois à un an
ÉlémentRecommandation CNILEn pratique
Opérations tracéescréation, consultation, modification et suppression des donnéestoutes les actions sur les données personnelles ou sensibles
Contenu d'une tracel'auteur individuellement identifié, l'horodatage, la nature de l'opération, la référence des données concernéesutil:lmartin;operation:update;table:Patient;id:4521;date:08/03/2027-08:05:40
Équipementle sujet 2025 a fait ajouter l'adresse IP de l'équipement;ip:10.0.1.21
Ce qu'on évitedupliquer dans les journaux les données du traitementla référence (l'identifiant) suffit ; jamais de mot de passe
Stockageséparé du système principal, accessible en écriture seule par l'application, sans écrasement possible ; journaux horodatés et signés dès leur créationserveur de journaux distinct, droits d'accès limités
Duréeentre six mois et un an dans le cas généralune durée fixée, justifiée et documentée
Exploitationun traitement automatique et une procédure qui génèrent et traitent des alertesseuils, tableau de bord, responsable désigné
Informationles utilisateurs sont informés de la journalisationmention à la connexion, charte

Compléter une méthode de journalisation

Le sujet 2025 faisait ajouter l'adresse IP à une méthode Java qui écrit dans un fichier de traces. Le même exercice en PHP, sur le modèle du sujet Kirassur, avec la superglobale $_SERVER :

php
1. /**
2.  * Écrit une action sensible dans le journal du jour
3.  * @param Utilisateur $util l'utilisateur connecté
4.  * @param Action $action l'action réalisée
5.  * @param int $idDossier le dossier concerné
6.  */
7. public function logAction(Utilisateur $util, Action $action, int $idDossier): void {
8.     $ligne = date('Y-m-d H:i:s')
9.            . ';util:' . $util->getId()
10.           . ';action:' . $action->getCode()
11.           . ';dossier:' . $idDossier
12.           . ';ip:' . $_SERVER['REMOTE_ADDR'];
13.    $fichier = '/var/log/toutassur/actions' . date('Ymd') . '.log';
14.    file_put_contents($fichier, $ligne . PHP_EOL, FILE_APPEND | LOCK_EX);
15. }

$_SERVER['REMOTE_ADDR'] donne l'adresse IP du client vue par le serveur (derrière un proxy inverse, c'est l'adresse du proxy : il faut alors lire l'en-tête transmis par le proxy, configuré pour être fiable). FILE_APPEND ajoute à la fin sans écraser, LOCK_EX évite que deux requêtes simultanées mélangent leurs lignes.

Lire un journal et repérer une attaque

Chaque attaque laisse une signature reconnaissable dans les journaux d'un serveur Web.

Signature dans le journalAttaque probable
des dizaines de POST /login en réponse 401, depuis la même adresse, en quelques secondesforce brute ou bourrage d'identifiants
des GET /api/contrats/1001, 1002, 1003… depuis le même clienténumération d'identifiants (contrôle d'accès défaillant si les réponses sont 200)
des centaines de 404 sur /admin, /wp-login.php, /.env, /backup.zipbalayage automatique à la recherche de fichiers sensibles
des apostrophes, UNION, OR 1=1, %27 dans les paramètres d'URLtentatives d'injection SQL
<script> ou %3Cscript%3E dans les paramètrestentatives de XSS
une connexion réussie à 3 h du matin depuis un pays inhabituelcompte compromis

Voici un extrait de journal à analyser :

text
185.22.14.9 - - [08/Mar/2027:02:14:01 +0100] "POST /login HTTP/1.1" 401 512
185.22.14.9 - - [08/Mar/2027:02:14:02 +0100] "POST /login HTTP/1.1" 401 512
185.22.14.9 - - [08/Mar/2027:02:14:02 +0100] "POST /login HTTP/1.1" 401 512
185.22.14.9 - - [08/Mar/2027:02:14:03 +0100] "POST /login HTTP/1.1" 200 1840
185.22.14.9 - - [08/Mar/2027:02:14:09 +0100] "GET /api/patients/2201 HTTP/1.1" 200 2210
185.22.14.9 - - [08/Mar/2027:02:14:10 +0100] "GET /api/patients/2202 HTTP/1.1" 200 2187
185.22.14.9 - - [08/Mar/2027:02:14:10 +0100] "GET /api/patients/2203 HTTP/1.1" 200 2304

Lecture : trois échecs de connexion en deux secondes, puis une réussite (un mot de passe deviné ou issu d'une fuite), puis des fiches patients lues dans l'ordre des identifiants, toutes en 200. Deux failles se cumulent : aucune limite de tentatives de connexion, et une API qui ne vérifie pas que le compte connecté a le droit de lire chaque fiche. Comme des données de santé ont été consultées, c'est aussi une violation de données à notifier.

Modéliser un système d'alertes

Le sujet Kirassur demandait de compléter un schéma de données pour déclencher une alerte quand une action sensible est répétée trop souvent (par exemple, « modifier les coordonnées bancaires d'un tiers », alerte à la troisième occurrence en cinq jours). Les informations à stocker, et où les mettre :

  • sur l'action sensible : l'intervalle de surveillance (en jours) et le nombre maximal d'appels sur cet intervalle ;
  • pour chaque exécution : l'utilisateur, l'action et l'horodatage ;
  • pour chaque alerte : l'utilisateur, l'action, la date et l'heure de déclenchement, l'état (nouvelle, affectée, résolue, classée), le responsable qui l'a prise en charge et la date d'affectation.
text
Action (id, libelle, estSensible, intervalleJours, nbMaxAppels)
Execution (id, idUtilisateur, idAction, dateHeure)
Alerte (id, idUtilisateur, idAction, dateHeureDeclenchement, etat,
        idResponsable, dateHeureAffectation)
    idResponsable et dateHeureAffectation : vides tant que l'alerte n'est pas affectée

Les tests unitaires dans les sujets

Un test unitaire vérifie automatiquement le comportement d'une petite unité de code (une méthode) sur un cas précis. Les sujets en donnent pour faire expliquer « les différents cas testés », ou demandent d'en écrire.

Assertion (JUnit, PHPUnit)Le test échoue si…
assertTrue(expr)expr est fausse
assertFalse(expr)expr est vraie
assertEquals(attendu, obtenu)les deux valeurs diffèrent
assertThrows(Exception.class, () -> …) (JUnit 5)aucune exception de ce type n'est levée

Pour expliquer un test, réponds à trois questions : quelle situation est préparée (souvent dans setUp), quel appel est fait, quel résultat est attendu et ce qu'il prouve. En 2025, trois tests de possedeDroit couvraient une table absente des droits (refus), une table et une opération autorisées (accord), une table connue avec une opération non autorisée (refus).

Un bon jeu de tests de sécurité couvre au moins : le cas autorisé, le cas refusé, et les cas limites (valeur vide, identifiant d'un autre utilisateur, jeton expiré). Un test qui ne vérifie que le cas qui marche ne prouve pas qu'un contrôle d'accès fonctionne.

Les autres tests de sécurité

TypePrincipeExemple
Test de non-régressionrejouer les tests existants après une modificationvérifier qu'un correctif n'a rien cassé
Test d'intrusion (pentest)attaquer l'application comme le ferait un attaquant, avec autorisationles charges utiles du sujet 2026 testées dans le formulaire de connexion
Analyse statique du code (SAST)un outil lit le code source et signale les motifs dangereuxune requête construite par concaténation
Analyse dynamique (DAST)un outil envoie des requêtes malveillantes à l'application en fonctionnementun scanner comme OWASP ZAP
Audit de codeun expert relit le codel'équipe « Audit et sécurité » du sujet 2026
Analyse des dépendancesun outil compare les bibliothèques aux failles publiéescomposer audit, npm audit

Le cours Tests traite l'écriture de tests unitaires et d'intégration en détail, et ses tests de sécurité prolongent cette section.

Pour aller plus loin. Le cours Journalisation et réponse à incident dit quoi journaliser et comment lire des logs ; les outils de test (SAST, DAST, SCA) sont présentés dans le chapitre Tests de sécurité du cours Sécurité applicative.

Exercices

Exercice 1 sur 2 Expliquer des tests Réussi À revoir

La méthode peutValider(Note note) de la classe Utilisateur renvoie vrai si l'utilisateur a le rôle « comptable » et n'est pas l'auteur de la note. Voici les tests :

java
@BeforeEach
void setUp() {
    compta = new Utilisateur(1, "comptable");
    salarie = new Utilisateur(2, "salarie");
    noteSalarie = new Note(10, salarie);
    noteCompta = new Note(11, compta);
}
@Test void test1() { assertTrue(compta.peutValider(noteSalarie)); }
@Test void test2() { assertFalse(salarie.peutValider(noteSalarie)); }
@Test void test3() { assertFalse(compta.peutValider(noteCompta)); }
  1. Explique le cas testé par chaque test.
  2. Écris la méthode peutValider. La classe Note a une méthode getAuteur() ; Utilisateur a getId() et getRole().
Voir la réponseMasquer la réponse de l'exercice 1
    • test1 : un comptable valide la note d'un autre salarié : c'est autorisé, la méthode doit renvoyer vrai.
    • test2 : un salarié sans le rôle comptable tente de valider (sa propre note) : refusé, faute de rôle.
    • test3 : un comptable tente de valider sa propre note : refusé, c'est la séparation des tâches.

    Les trois tests couvrent le cas autorisé et les deux raisons de refus.

  1. La méthode :

    java
    public boolean peutValider(Note note) {
        return this.role.equals("comptable")
            && note.getAuteur().getId() != this.id;
    }
    

    equals compare le contenu des chaînes ; == comparerait des références. Les identifiants sont des int, comparés avec !=.

Tu avais trouvé ?

Exercice 2 sur 2 Analyser un journal Réussi À revoir

Voici un extrait du journal d'un serveur Web :

text
77.140.3.18 - - [12/Apr/2027:10:02:11 +0200] "GET /produits.php?cat=3 HTTP/1.1" 200 4120
77.140.3.18 - - [12/Apr/2027:10:02:19 +0200] "GET /produits.php?cat=3%27 HTTP/1.1" 500 310
77.140.3.18 - - [12/Apr/2027:10:02:31 +0200] "GET /produits.php?cat=3%20OR%201=1 HTTP/1.1" 200 98410
77.140.3.18 - - [12/Apr/2027:10:03:02 +0200] "GET /produits.php?cat=3%20UNION%20SELECT%20login,mdp%20FROM%20client HTTP/1.1" 500 310
77.140.3.18 - - [12/Apr/2027:10:03:20 +0200] "GET /produits.php?cat=3%20UNION%20SELECT%20login,mdp,null%20FROM%20client HTTP/1.1" 200 15200

Sachant que %27 code une apostrophe et %20 une espace, décris l'attaque étape par étape et ce que l'attaquant a probablement obtenu.

Voir la réponseMasquer la réponse de l'exercice 2
  1. 10:02:11 : requête normale, catégorie 3.
  2. 10:02:19 : une apostrophe ajoutée provoque une erreur 500 : le paramètre est inséré dans une requête SQL sans protection. L'attaquant vient de détecter la faille.
  3. 10:02:31 : 3 OR 1=1 renvoie une page de 98 410 octets au lieu de 4 120 : la condition toujours vraie affiche tous les produits. L'injection est confirmée (le paramètre est numérique, sans apostrophes autour).
  4. 10:03:02 : un UNION à deux colonnes échoue (500) : le nombre de colonnes ne correspond pas à la requête d'origine.
  5. 10:03:20 : avec trois colonnes, le UNION réussit (200, 15 200 octets) : l'attaquant a très probablement affiché les identifiants et les mots de passe (ou leurs empreintes) de la table client.

Conséquences : requête préparée à mettre en place d'urgence, réinitialisation des mots de passe des clients, analyse de la violation de données et notification à la CNIL si elle présente un risque pour les personnes.

Tu avais trouvé ?

Chapitre terminé !

Fais les 2 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Lire et écrire du code sur papierRevoir le sommaire du cours