Présentation du cas
Une association souhaite une petite application web permettant à ses adhérents de s’inscrire à des ateliers. Un espace d’administration protégé par identifiant et mot de passe affiche la liste des inscrits. L’application est développée en PHP, avec une base MariaDB.
Environnement de recette
| Élément | Valeur | Remarque |
|---|---|---|
| Serveur | web-dev01 (Debian 12) | Apache 2.4, PHP 8.2, MariaDB 10.11 |
| URL de recette | http://inscriptions.lab.local | Environnement interne de recette |
| Poste de test | dev-pc01 | Firefox, terminal avec curl |
| Version testée | v1.0.0 (commit a3f9c2e) | Version identifiée dans le dépôt Git |
| Jeu de données | 3 ateliers, 12 inscrits fictifs | Aucune donnée réelle |
Exigences retenues
| Réf. | Exigence du cahier des charges |
|---|---|
| E1 | Un adhérent peut s’inscrire à un atelier ; l’inscription est enregistrée en base. |
| E2 | Les saisies sont contrôlées côté serveur (champs obligatoires, format de l’e-mail), et pas seulement dans le navigateur. |
| E3 | L’application résiste aux injections SQL. |
| E4 | Les données saisies par les utilisateurs sont affichées sans pouvoir exécuter de script (protection XSS). |
| E5 | Les pages d’administration ne sont accessibles qu’après authentification. |
| E6 | Les mots de passe des administrateurs sont stockés sous forme d’empreinte (hachage), jamais en clair. |
| E7 | Les échanges avec l’application sont chiffrés (HTTPS). |
Fiches de test et preuves
Test 1 : inscription valide à un atelier
| Fiche de test n° 1 | |
|---|---|
| Exigence couverte | E1 |
| Objectif du test | Vérifier qu’une inscription correcte est acceptée et enregistrée. |
| Type de test | Fonctionnel |
| Prérequis | Application déployée ; atelier « Initiation Linux » ouvert. |
| Procédure | 1. Ouvrir la page d’inscription dans Firefox. 2. Saisir : Martin / Léa / lea.martin@example.org / Initiation Linux, puis valider. 3. Contrôler l’enregistrement dans la table inscrit. |
| Résultat attendu | Message de confirmation affiché ; une nouvelle ligne en base avec les valeurs saisies et la date du jour. |
| Résultat obtenu | Conforme. |
| Preuve(s) | Captures 1a et 1b |
| Statut | ☒ Conforme |
| Anomalie / commentaire | Aucune. |
| Re-test après correction | Sans objet. |
Capture 1a : réponse de l’application (code source de la page, extrait)
<main>
<p class="succes">Votre inscription à l'atelier « Initiation Linux » est enregistrée.</p>
<a href="index.php">Retour à la liste des ateliers</a>
</main>
Capture 1b : contrôle en base de données
MariaDB [inscriptions]> SELECT id, nom, prenom, email, id_atelier, date_inscription
-> FROM inscrit ORDER BY id DESC LIMIT 1;
+----+--------+--------+------------------------+------------+---------------------+
| id | nom | prenom | email | id_atelier | date_inscription |
+----+--------+--------+------------------------+------------+---------------------+
| 13 | Martin | Léa | lea.martin@example.org | 2 | 2026-10-15 14:02:11 |
+----+--------+--------+------------------------+------------+---------------------+
Test 2 : contrôle des saisies côté serveur
| Fiche de test n° 2 | |
|---|---|
| Exigence couverte | E2 |
| Objectif du test | Vérifier que le serveur refuse une saisie invalide, même si les contrôles du navigateur sont contournés. |
| Type de test | Fonctionnel / Sécurité |
| Prérequis | Test 1 conforme. 13 inscrits en base. |
| Procédure | 1. Envoyer directement au serveur, avec curl, un formulaire sans prénom et avec un e-mail invalide. 2. Compter les inscrits en base. |
| Résultat attendu | Deux messages d’erreur ; aucune nouvelle ligne en base (toujours 13). |
| Résultat obtenu | Conforme. |
| Preuve(s) | Capture 2 |
| Statut | ☒ Conforme |
| Anomalie / commentaire | Aucune. |
| Re-test après correction | Sans objet. |
Capture 2 : envoi d’une saisie invalide sans passer par le navigateur
dev@dev-pc01:~$ curl -s -X POST http://inscriptions.lab.local/inscription.php \
-d 'nom=Test&prenom=&email=pas-un-email&id_atelier=2' | grep 'class="erreur"'
<p class="erreur">Le prénom est obligatoire.</p>
<p class="erreur">L'adresse e-mail n'est pas valide.</p>
MariaDB [inscriptions]> SELECT COUNT(*) FROM inscrit;
| COUNT(*) | 13 |
Test 3 : résistance à l’injection SQL
| Fiche de test n° 3 | |
|---|---|
| Exigence couverte | E3 |
| Objectif du test | Vérifier qu’une saisie piégée dans l’identifiant ne permet pas de contourner l’authentification. |
| Type de test | Sécurité |
| Prérequis | Compte administrateur « gestionnaire » existant. |
| Procédure | 1. Envoyer au formulaire de connexion l’identifiant admin' OR '1'='1' -- et un mot de passe quelconque.2. Relire la requête de connexion dans le code source. |
| Résultat attendu | Connexion refusée ; requête préparée avec paramètre lié. |
| Résultat obtenu | Conforme. |
| Preuve(s) | Captures 3a et 3b |
| Statut | ☒ Conforme |
| Anomalie / commentaire | Aucune. |
| Re-test après correction | Sans objet. |
Capture 3a : tentative d’injection
dev@dev-pc01:~$ curl -s -X POST http://inscriptions.lab.local/admin/login.php \
--data-urlencode "login=admin' OR '1'='1' -- " --data-urlencode 'mdp=x' \
| grep -E 'erreur|Tableau de bord'
<p class="erreur">Identifiant ou mot de passe incorrect.</p>
Capture 3b : requête de connexion (admin/login.php, extrait)
$req = $pdo->prepare('SELECT id, mdp_hash FROM utilisateur WHERE login = :login');
$req->execute(['login' => $_POST['login']]);
$user = $req->fetch();
Test 4 : protection contre l’injection de script (XSS)
| Fiche de test n° 4 | |
|---|---|
| Exigence couverte | E4 |
| Objectif du test | Vérifier qu’un script saisi dans le nom n’est pas exécuté lors de l’affichage dans l’espace d’administration. |
| Type de test | Sécurité |
| Prérequis | Tests 1 à 3 conformes. |
| Procédure | 1. S’inscrire avec le nom <script>alert('XSS')</script>.2. Se connecter à l’espace d’administration et ouvrir la liste des inscrits. 3. Afficher le code source de la page. |
| Résultat attendu | Aucune fenêtre d’alerte ; le nom apparaît sous forme de texte, avec les caractères < et > échappés. |
| Résultat obtenu | 1er passage : fenêtre « XSS » affichée (non conforme). 2e passage : texte échappé. |
| Preuve(s) | Captures 4a et 4b |
| Statut | ☒ Conforme après correction |
| Anomalie / commentaire | Anomalie bloquante (sécurité) : faille XSS stockée dans admin/liste.php, les noms étaient affichés sans échappement. Corrigée avec htmlspecialchars(). |
| Re-test après correction | 15/10/2026 — Conforme (capture 4b), et test 1 rejoué sans régression. |
Capture 4a : premier passage, code source de admin/liste.php (extrait)
<tr>
<td><script>alert('XSS')</script></td>
<td>example</td>
<td>test.xss@example.org</td>
</tr>
Correction appliquée dans admin/liste.php (même traitement pour le prénom et l’e-mail)
// Avant
<td><?= $inscrit['nom'] ?></td>
// Après : échappement de toutes les données affichées
<td><?= htmlspecialchars($inscrit['nom'], ENT_QUOTES, 'UTF-8') ?></td>
Capture 4b : re-test, code source après correction (extrait)
<tr>
<td><script>alert('XSS')</script></td>
<td>example</td>
<td>test.xss@example.org</td>
</tr>
Test 5 : accès à l’espace d’administration
| Fiche de test n° 5 | |
|---|---|
| Exigence couverte | E5 |
| Objectif du test | Vérifier qu’une page d’administration n’est pas accessible sans être connecté. |
| Type de test | Droits et accès |
| Prérequis | Aucune session ouverte sur le poste de test. |
| Procédure | 1. Demander directement la page admin/liste.php avec curl, sans cookie de session. |
| Résultat attendu | Redirection (code 302) vers login.php ; aucune donnée d’inscrit renvoyée. |
| Résultat obtenu | Conforme. |
| Preuve(s) | Capture 5 |
| Statut | ☒ Conforme |
| Anomalie / commentaire | Aucune. |
| Re-test après correction | Sans objet. |
Capture 5 : accès direct sans authentification
dev@dev-pc01:~$ curl -s -i http://inscriptions.lab.local/admin/liste.php | head -5
HTTP/1.1 302 Found
Date: Thu, 15 Oct 2026 14:31:07 GMT
Server: Apache
Location: login.php
Content-Length: 0
Test 6 : stockage des mots de passe
| Fiche de test n° 6 | |
|---|---|
| Exigence couverte | E6 |
| Objectif du test | Vérifier que les mots de passe des administrateurs sont hachés. |
| Type de test | Sécurité |
| Prérequis | Compte « gestionnaire » créé via l’application. |
| Procédure | 1. Lire la colonne mdp_hash de la table utilisateur.2. Relire le code de création et de vérification du mot de passe. |
| Résultat attendu | Empreinte bcrypt (préfixe $2y$) ; utilisation de password_hash() et password_verify(). |
| Résultat obtenu | Conforme. |
| Preuve(s) | Capture 6 |
| Statut | ☒ Conforme |
| Anomalie / commentaire | Aucune. |
| Re-test après correction | Sans objet. |
Capture 6 : contenu de la table et code associé
MariaDB [inscriptions]> SELECT login, mdp_hash FROM utilisateur;
+--------------+------------------------------------------------------+
| login | mdp_hash |
+--------------+------------------------------------------------------+
| gestionnaire | $2y$10$███████████████████████████████████████████ |
+--------------+------------------------------------------------------+
// admin/creer_utilisateur.php
$hash = password_hash($_POST['mdp'], PASSWORD_DEFAULT);
// admin/login.php
if ($user && password_verify($_POST['mdp'], $user['mdp_hash'])) {
Test 7 : chiffrement des échanges (HTTPS)
| Fiche de test n° 7 | |
|---|---|
| Exigence couverte | E7 |
| Objectif du test | Vérifier que l’application est servie en HTTPS. |
| Type de test | Sécurité |
| Prérequis | Aucun. |
| Procédure | 1. Demander l’application en HTTPS avec curl. |
| Résultat attendu | Réponse HTTPS valide avec un certificat reconnu ; redirection automatique de HTTP vers HTTPS. |
| Résultat obtenu | Connexion HTTPS refusée : le serveur de recette n’écoute qu’en HTTP. |
| Preuve(s) | Capture 7 |
| Statut | ☒ Conforme avec réserve |
| Anomalie / commentaire | Anomalie majeure : HTTPS non configuré. Acceptée en réserve, car l’environnement de recette est interne et isolé ; mise en place d’un certificat obligatoire avant la mise en production. |
| Re-test après correction | À réaliser lors de la levée de la réserve. |
Capture 7 : tentative de connexion en HTTPS
dev@dev-pc01:~$ curl -sS -I https://inscriptions.lab.local/
curl: (7) Failed to connect to inscriptions.lab.local port 443: Connection refused
Synthèse des tests
| N° | Test | Statut | Anomalie / réserve | Re-test |
|---|---|---|---|---|
| 1 | Inscription valide | Conforme | — | — |
| 2 | Contrôle des saisies côté serveur | Conforme | — | — |
| 3 | Injection SQL | Conforme | — | — |
| 4 | Injection de script (XSS) | Conforme après correction | Bloquante : XSS stockée | Conforme |
| 5 | Accès à l’administration | Conforme | — | — |
| 6 | Stockage des mots de passe | Conforme | — | — |
| 7 | Chiffrement HTTPS | Conforme avec réserve | Majeure : HTTPS absent | À faire |
Procès-verbal de recette
| Procès-verbal de recette | |
|---|---|
| Projet / installation | Application web d’inscription aux ateliers — version v1.0.1 |
| Bilan des tests | 7 tests réalisés : 6 conformes (dont 1 après correction), 0 non conforme, 1 avec réserve |
| Décision | ☐ Recette prononcée sans réserve ☒ Recette prononcée avec réserves ☐ Recette refusée |
| Réserves éventuelles | R1 — Mettre en place HTTPS (certificat valide et redirection HTTP vers HTTPS) avant toute mise en production. Échéance : avant le déploiement. La mise en production est conditionnée à la levée de cette réserve. |
| Observations | Pistes d’amélioration hors exigences : jeton anti-CSRF sur les formulaires, réduction de l’en-tête Server, limitation des tentatives de connexion. |
| Testeur | Nom Prénom — 15/10/2026 — signature |
| Valideur | Nom Prénom — 15/10/2026 — signature |
🧠 Testez vos connaissances
5 questions. Répondez puis cliquez sur « Corriger ». Rien n’est enregistré : c’est de l’auto-évaluation.
© 2026 Bruno Jousseaume. Ce cours est publié sous licence CC BY-NC-SA 4.0 : vous pouvez le partager et l’adapter en citant l’auteur et un lien vers cette page, sans usage commercial, et en partageant vos adaptations sous la même licence.