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 ».
| Élément | Recommandation CNIL | En pratique |
|---|---|---|
| Opérations tracées | création, consultation, modification et suppression des données | toutes les actions sur les données personnelles ou sensibles |
| Contenu d'une trace | l'auteur individuellement identifié, l'horodatage, la nature de l'opération, la référence des données concernées | util:lmartin;operation:update;table:Patient;id:4521;date:08/03/2027-08:05:40 |
| Équipement | le sujet 2025 a fait ajouter l'adresse IP de l'équipement | ;ip:10.0.1.21 |
| Ce qu'on évite | dupliquer dans les journaux les données du traitement | la référence (l'identifiant) suffit ; jamais de mot de passe |
| Stockage | sé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éation | serveur de journaux distinct, droits d'accès limités |
| Durée | entre six mois et un an dans le cas général | une durée fixée, justifiée et documentée |
| Exploitation | un traitement automatique et une procédure qui génèrent et traitent des alertes | seuils, tableau de bord, responsable désigné |
| Information | les utilisateurs sont informés de la journalisation | mention à 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 :
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 journal | Attaque probable |
|---|---|
des dizaines de POST /login en réponse 401, depuis la même adresse, en quelques secondes | force 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.zip | balayage automatique à la recherche de fichiers sensibles |
des apostrophes, UNION, OR 1=1, %27 dans les paramètres d'URL | tentatives d'injection SQL |
<script> ou %3Cscript%3E dans les paramètres | tentatives de XSS |
| une connexion réussie à 3 h du matin depuis un pays inhabituel | compte compromis |
Voici un extrait de journal à analyser :
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.
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é
| Type | Principe | Exemple |
|---|---|---|
| Test de non-régression | rejouer les tests existants après une modification | vérifier qu'un correctif n'a rien cassé |
| Test d'intrusion (pentest) | attaquer l'application comme le ferait un attaquant, avec autorisation | les 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 dangereux | une requête construite par concaténation |
| Analyse dynamique (DAST) | un outil envoie des requêtes malveillantes à l'application en fonctionnement | un scanner comme OWASP ZAP |
| Audit de code | un expert relit le code | l'équipe « Audit et sécurité » du sujet 2026 |
| Analyse des dépendances | un outil compare les bibliothèques aux failles publiées | composer 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 :
@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)); }
- Explique le cas testé par chaque test.
- Écris la méthode
peutValider. La classeNotea une méthodegetAuteur();UtilisateuragetId()etgetRole().
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.
-
La méthode :
public boolean peutValider(Note note) { return this.role.equals("comptable") && note.getAuteur().getId() != this.id; }equalscompare le contenu des chaînes ;==comparerait des références. Les identifiants sont desint, 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 :
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
- 10:02:11 : requête normale, catégorie 3.
- 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.
- 10:02:31 :
3 OR 1=1renvoie 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). - 10:03:02 : un
UNIONà deux colonnes échoue (500) : le nombre de colonnes ne correspond pas à la requête d'origine. - 10:03:20 : avec trois colonnes, le
UNIONréussit (200, 15 200 octets) : l'attaquant a très probablement affiché les identifiants et les mots de passe (ou leurs empreintes) de la tableclient.
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é ?