Security Misconfiguration (mauvaise configuration de sécurité)

- Comprendre comment une application peut rester vulnérable même sans erreur dans son code, à cause de la façon dont elle est configurée et déployée (Security Misconfiguration, n°2 du Top 10 de l'OWASP (2025)).
- Apprendre à retirer les réglages de développement avant la mise en production : pages de diagnostic, messages d'erreur détaillés, comptes par défaut.
Un code parfaitement sécurisé peut quand même tout révéler si son environnement ne l'est pas : une page oubliée, une option de débogage laissée active, ou un compte jamais reconfiguré suffisent à donner à un attaquant tout ce dont il a besoin.
1. Création du projet vulnérable
Créez un dossier tp-config contenant trois fichiers : des réglages laissés tels quels depuis le
développement du site du club de randonnée.
diagnostic.php, une page créée pendant le développement pour vérifier la configuration du
serveur, jamais retirée :
<?php
// Page créée pendant le développement, pour vérifier la configuration du serveur
phpinfo();
statistiques.php, qui affiche le nombre d'adhérents du club :
<?php
// Affiche quelques statistiques du club de randonnée
$pdo = new PDO('mysql:host=sql.club-randonnee.interne;dbname=club_randonnee', 'club_app', 'R@ndo2019!');
$nombreAdherents = $pdo->query('SELECT COUNT(*) FROM adherent')->fetchColumn();
echo "Nombre d'adhérents : $nombreAdherents";
admin.php, l'espace d'administration du club :
<?php
session_start();
$erreur = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Compte administrateur créé lors de l'installation du site, pour les premiers tests
if ($_POST['pseudo'] === 'admin' && $_POST['mot_de_passe'] === 'admin') {
$_SESSION['admin'] = true;
echo "Bienvenue dans l'administration du club de randonnée.";
exit;
}
$erreur = 'Identifiants incorrects.';
}
?>
<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><title>Administration - Club de randonnée</title></head>
<body>
<h1>Administration du club de randonnée</h1>
<?php if ($erreur): ?><p style="color:red;"><?= htmlspecialchars($erreur) ?></p><?php endif; ?>
<form method="post" action="">
<p><label>Pseudo : <input type="text" name="pseudo"></label></p>
<p><label>Mot de passe : <input type="password" name="mot_de_passe"></label></p>
<button type="submit">Se connecter</button>
</form>
</body>
</html>
sql.club-randonnee.interne ne correspond à aucun serveur réellement joignable dans ce TP : c'est
volontaire, pour provoquer une erreur de connexion à l'étape 3, exactement comme le ferait une
base de données momentanément indisponible sur un vrai site.
2. Démarrer le serveur Web
Dans le dossier tp-config, démarrez le serveur Web intégré à PHP, en activant volontairement
l'affichage des erreurs (ce qui correspond au réglage display_errors = On d'un php.ini laissé
en configuration de développement) :
php -S localhost:8000 -d display_errors=1
3. Repérer une page de diagnostic oubliée
Ouvrez http://localhost:8000/diagnostic.php dans votre navigateur.
Question : Quelles informations cette page révèle-t-elle ? En quoi peuvent-elles aider un attaquant, alors qu'elles ne contiennent ni mot de passe ni donnée personnelle ?
📌 Une réponse
diagnostic.php est un exemple classique de fonctionnalité de développement oubliée en
production : utile pour déboguer en local, dangereuse si elle reste accessible à n'importe qui,
une fois le site mis en ligne.
4. Provoquer une erreur verbeuse
Ouvrez maintenant http://localhost:8000/statistiques.php.
Résultat attendu : une erreur PHP s'affiche directement dans le navigateur, avec une pile d'appels (stack trace) complète.
Question : Sans lire aucun mot de passe (PHP le masque automatiquement dans ce message d'erreur depuis la version 8.2), quelles informations exploitables un attaquant obtient-il quand même de ce message ?
📌 Une réponse
Depuis PHP 8.2, les paramètres marqués #[\SensitiveParameter] (dont le mot de passe de
PDO::__construct()) sont automatiquement masqués dans les messages d'erreur et les traces
d'appel. C'est une excellente protection, mais elle ne concerne que ce paramètre précis : tout
le reste du message (chemins, hôtes, noms d'utilisateur) continue d'être affiché tel quel si
display_errors est activé.
5. Utiliser le compte administrateur par défaut
Ouvrez http://localhost:8000/admin.php et connectez-vous avec le pseudo admin et le mot de passe
admin.
Résultat attendu : la connexion réussit immédiatement, sans qu'aucun mot de passe n'ait été deviné ni cassé.
Question : D'où vient ce compte, et pourquoi est-il si dangereux qu'il ait des identifiants aussi simples ?
📌 Une réponse
Contrairement aux TP précédents (injection, XSS, CSRF, force brute), aucune attaque technique n'a été nécessaire ici : les trois failles de ce TP s'exploitent en lisant une page, en provoquant une erreur normale, ou en essayant le compte le plus évident. C'est ce qui rend la mauvaise configuration si fréquente : elle ne demande aucune compétence particulière à exploiter.
6. Supprimer la page de diagnostic
La première correction est la plus simple : une page de diagnostic n'a jamais sa place sur un
site en production. Supprimez le fichier diagnostic.php.
Rechargez http://localhost:8000/diagnostic.php.
Résultat attendu : une erreur 404 (page non trouvée) s'affiche : il n'y a plus rien à découvrir.
Ne protégez pas une page de diagnostic par un mot de passe « temporaire » en pensant la retirer plus tard : avec le temps, ces pages sont oubliées et finissent par rester accessibles pendant des années. La bonne pratique consiste à les supprimer du serveur de production dès que leur usage de développement n'est plus nécessaire.
7. Masquer les erreurs et les journaliser
Modifiez tp-config/statistiques.php pour cesser d'afficher les erreurs au visiteur, tout en
conservant le détail dans un fichier journal, utile à l'équipe technique :
<?php
// En production, l'affichage des erreurs est désactivé et les erreurs sont journalisées
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/erreurs.log');
try {
$pdo = new PDO('mysql:host=sql.club-randonnee.interne;dbname=club_randonnee', 'club_app', 'R@ndo2019!');
$nombreAdherents = $pdo->query('SELECT COUNT(*) FROM adherent')->fetchColumn();
echo "Nombre d'adhérents : $nombreAdherents";
} catch (PDOException $e) {
error_log('Erreur base de données : ' . $e->getMessage());
http_response_code(500);
echo "Une erreur est survenue. Merci de réessayer plus tard.";
}
Rechargez http://localhost:8000/statistiques.php, puis ouvrez le fichier tp-config/erreurs.log.
Résultat attendu : le navigateur affiche seulement « Une erreur est survenue. Merci de réessayer
plus tard. », tandis que erreurs.log contient le détail complet de l'erreur (hôte, utilisateur,
message exact).
Question : Pourquoi ne faut-il pas simplement supprimer l'information d'erreur, plutôt que de la déplacer vers un fichier journal ?
📌 Une réponse
8. Supprimer le compte par défaut
Modifiez enfin tp-config/admin.php pour remplacer le compte codé en dur par un vrai compte,
avec un mot de passe haché et propre au club :
<?php
session_start();
function trouverAdmin(string $pseudo): ?array
{
$admins = json_decode(file_get_contents(__DIR__ . '/admin.json'), true);
foreach ($admins as $admin) {
if ($admin['pseudo'] === $pseudo) {
return $admin;
}
}
return null;
}
$erreur = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$admin = trouverAdmin($_POST['pseudo'] ?? '');
if ($admin && password_verify($_POST['mot_de_passe'] ?? '', $admin['mot_de_passe'])) {
$_SESSION['admin'] = true;
echo "Bienvenue dans l'administration du club de randonnée.";
exit;
}
$erreur = 'Identifiants incorrects.';
}
?>
<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><title>Administration - Club de randonnée</title></head>
<body>
<h1>Administration du club de randonnée</h1>
<?php if ($erreur): ?><p style="color:red;"><?= htmlspecialchars($erreur) ?></p><?php endif; ?>
<form method="post" action="">
<p><label>Pseudo : <input type="text" name="pseudo"></label></p>
<p><label>Mot de passe : <input type="password" name="mot_de_passe"></label></p>
<button type="submit">Se connecter</button>
</form>
</body>
</html>
avec un fichier admin.json contenant le nouveau compte :
[
{
"pseudo": "admin",
"mot_de_passe": "$2y$12$rLs4Xb0XZy336OLQmLobw.VNJmSwoCcVX7oWdhDWWj7aEhFhdwkD."
}
]
Rejouez l'étape 5 avec admin / admin.
Résultat attendu : la connexion est refusée. Le mot de passe réel de ce compte (que vous ne
connaissez pas, comme un attaquant) a été choisi au moment de la mise en production, puis haché
avec password_hash(), comme vu dans la séance sur le
hachage sécurisé.
Un compte créé pour l'installation ou les premiers tests doit soit être supprimé, soit recevoir un mot de passe unique et robuste avant la mise en production. Cette règle vaut aussi pour les comptes des logiciels tiers installés sur le serveur (base de données, outils d'administration...), qui sont souvent livrés avec un identifiant et un mot de passe par défaut, documentés publiquement.
Notions théoriques
La Security Misconfiguration (« mauvaise configuration de sécurité ») regroupe les failles qui ne viennent pas d'une erreur dans le code, mais de la façon dont l'application, le serveur ou les services qui l'entourent sont réglés et déployés.
Contrairement aux Authentication Failures, qui portent sur la façon dont une identité est vérifiée, la mauvaise configuration peut rendre une application vulnérable même si son code d'authentification est irréprochable.
Les principales formes de mauvaise configuration
- Fonctionnalités de développement oubliées : pages de diagnostic, comptes de test, jeux de données d'exemple, laissés accessibles après la mise en ligne.
- Comptes par défaut non modifiés : identifiants fournis par un logiciel (base de données, CMS, outil d'administration) à l'installation, jamais changés.
- Messages d'erreur trop détaillés : chemins de fichiers, requêtes SQL, versions de logiciels affichés directement à l'utilisateur.
- En-têtes de sécurité absents ou mal réglés : voir la séance sur les vulnérabilités des headers HTTP, qui détaille cette partie de la mauvaise configuration, au niveau du serveur Web lui-même.
- Environnements de développement et de production mal distingués : les mêmes identifiants, les mêmes réglages permissifs utilisés partout.
- Permissions excessives sur un service cloud (par exemple un espace de stockage accessible à tous, alors qu'il ne devrait l'être qu'à l'application).
- Mises à jour de sécurité non appliquées, qui laissent actives des failles déjà corrigées par l'éditeur du logiciel.
Pourquoi cette faille est-elle si fréquente ?
D'après l'OWASP, en 2025, la totalité des applications testées présentaient au moins une forme de mauvaise configuration. Cette fréquence s'explique par un paradoxe : plus un système est configurable (ce qui est généralement une qualité), plus il offre de réglages qu'il est possible d'oublier ou de mal renseigner. Une seule case cochée par erreur, ou un seul fichier oublié, suffit.
Comment se protéger contre la mauvaise configuration ?
- Un processus de déploiement reproductible, qui installe toujours la même configuration durcie, plutôt que des réglages ajustés manuellement serveur par serveur.
- Des environnements de développement et de production identiquement configurés, avec des identifiants différents dans chacun.
- Une installation minimale : ne garder que les fonctionnalités, bibliothèques et comptes réellement nécessaires au fonctionnement du site.
- La désactivation de l'affichage des erreurs en production, combinée à une journalisation systématique.
- Un changement systématique des identifiants par défaut, pour l'application comme pour tous les services qu'elle utilise.
- Une vérification régulière et automatisée de la configuration effectivement en place.
Un site protégé contre l'injection SQL, le XSS et le CSRF, mais dont une page de diagnostic oubliée affiche encore la version exacte du serveur et le chemin complet de ses fichiers, facilite grandement la recherche d'une autre faille à exploiter. La mauvaise configuration n'est généralement pas la faille finale d'une attaque, mais souvent celle qui rend les autres possibles ou plus faciles.
Test de mémorisation/compréhension
Bonnes pratiques
- Ne jamais déployer de page ou de compte de développement sur un serveur de production : supprimer plutôt que masquer.
- Désactiver
display_errorsen production, et journaliser systématiquement les erreurs côté serveur (log_errors,error_log). - Changer tous les identifiants par défaut, pour l'application comme pour les services qu'elle utilise (base de données, outils d'administration...).
- Configurer les environnements de développement et de production de façon identique, avec des identifiants différents.
- Automatiser le déploiement pour obtenir une configuration reproductible, plutôt que des réglages ajustés à la main.
- Vérifier régulièrement la configuration réellement en place (en-têtes de sécurité, permissions, mises à jour disponibles).
Un fichier de configuration contenant de vrais identifiants (base de données, clé d'API) ne doit jamais être envoyé dans un dépôt Git public. Un historique Git mal nettoyé peut continuer à exposer d'anciens identifiants, même après leur suppression du fichier actuel.
Ce que fait Symfony
Symfony distingue automatiquement les environnements grâce à la variable APP_ENV (dev,
prod...) : en environnement prod, les erreurs ne sont jamais affichées en détail au
visiteur (une page générique est montrée), alors qu'elles le sont largement en dev, pour faciliter
le travail du développeur.
# .env (jamais commité avec de vraies valeurs en production)
APP_ENV=prod
APP_DEBUG=0
Le bundle Symfony Maker ne génère également aucun compte ni aucune page de diagnostic par
défaut : chaque compte administrateur doit être créé explicitement, avec son propre mot de passe,
haché via UserPasswordHasherInterface.
Le fichier .env ne doit contenir aucun secret réel lorsqu'il est versionné avec le code : les
valeurs sensibles (mots de passe, clés d'API) se placent dans un fichier .env.local, exclu du
dépôt Git par le .gitignore fourni avec Symfony.