Leçon 14/14 10 min

Logs, erreurs et déploiement

Une page de debug oubliée livre le serveur. Lis les secrets, puis la checklist OWASP de mise en prod qui clôt le cours.

La page de debug qui offre le serveur

Un site en production plante sur une erreur. Au lieu d'un message sobre, le visiteur tombe sur une belle page de debug, celle des frameworks (Laravel, Symfony, Django) : l'exception, la trace complète avec les chemins des fichiers, et, jackpot, les variables d'environnement, dont DB_PASSWORD et la clé secrète de l'application.

L'attaquant n'a même pas eu à attaquer : le site lui a tendu sa propre carte. Identifiants de la base, version du framework (donc les failles connues, leçon 13), structure interne : tout est là, à l'écran. Laisser le mode debug activé en production est la mauvaise configuration la plus courante du web.

Cette dernière leçon parle de la mise en production : logger sans fuiter, gérer les erreurs sans tout révéler, et la checklist qui noue les treize leçons précédentes. C'est lié à l'OWASP 2025 (A02 Security Misconfiguration, A09 Logging & Alerting Failures et A10 Mishandling of Exceptional Conditions).

Les trois fautes de la mise en prod

1. Trop en dire dans les erreurs. Une trace détaillée affiche le framework, les chemins, la requête SQL, parfois les secrets. C'est une carte offerte à l'attaquant. En production, le visiteur ne doit voir qu'un message générique ; les détails partent dans le log, côté serveur.

2. Mal logger. Deux excès opposés, aussi dangereux :

  • Trop : logger les mots de passe, les tokens, le corps des requêtes, les données personnelles. Les logs sont souvent moins protégés que la base. Ils sont copiés vers des agrégateurs tiers et lus par beaucoup de monde. Un secret dans un log est un secret qui fuite.
  • Trop peu : ne rien logger des connexions, des échecs d'authentification, des refus d'accès. On devient aveugle : impossible de détecter une intrusion, ni d'enquêter après coup.

3. Le mode debug en production. La faute numéro un, celle du début. Les pages de debug des frameworks déballent l'environnement complet, secrets compris. La règle est sans appel : APP_DEBUG=false, display_errors=Off, toujours, en prod.

Le « ce n'est qu'un message d'erreur ». Une erreur verbeuse n'est jamais cosmétique. Elle livre la version du framework (donc les CVE à essayer, leçon 13), les chemins du serveur, la structure de la base, parfois une requête SQL entière. L'attaquant cartographie votre application sans le moindre effort, juste en la faisant planter.

À vous d'attaquer : lisez les secrets dans l'erreur

Voici une app qui va planter (une requête en base échoue). Réglez le mode debug, puis déclenchez l'erreur, et regardez ce que le visiteur voit. En debug, vous lirez les identifiants de la base ; en production, juste un message neutre. Tout est simulé.

monsite.com · une erreur va se produire

        
Ce qui vient de se passer

En debug, la page d'erreur déballe tout : l'exception, les chemins des fichiers, la version, la requête SQL, et surtout les variables d'environnement avec le mot de passe de la base. L'attaquant n'a plus qu'à se servir. C'est la mauvaise configuration la plus fréquente, et l'une des plus graves.

En production, le visiteur ne voit qu'un message neutre avec une référence d'incident. Les détails (l'exception, la requête) sont bien enregistrés, mais dans le log serveur, hors de portée du visiteur. Vous pouvez débugger ; l'attaquant, lui, n'apprend rien.

Le correctif : déployer proprement

Les erreurs. En production, un message générique à l'écran, les détails dans le log :

// production : aucune erreur technique à l'écran
ini_set('display_errors', '0');
// + APP_DEBUG=false (Laravel/Symfony), DEBUG=False (Django)
// → le visiteur voit « Une erreur est survenue (réf. #a3f1) »
// → l'exception complète part dans le log, côté serveur

Les logs. On enregistre les événements de sécurité (connexion, échec d'auth, refus d'accès, changement de privilège) avec le contexte utile (qui, quoi, quand, d'où). Mais on n'enregistre jamais de secret, de mot de passe, de token ni de donnée personnelle brute. On masque (redact), on centralise, on alerte, on conserve.

// BIEN : un événement utile, sans secret
$log->warning('login_failed', ['user' => $email, 'ip' => $ip, 'at' => now()]);
// JAMAIS : $log->info('login', ['password' => $password]);  ← le secret fuite dans le log

Le déploiement. La sécurité ne se vérifie pas « au feeling » : on déroule une liste. Debug coupé, secrets en variables d'environnement (leçon 12), HTTPS et en-têtes (leçon 11), moindre privilège (leçon 5), dépendances à jour (leçon 13), et on supprime ce qui traîne : .git/ exposé, .env accessible, /phpinfo.php, sauvegardes, comptes par défaut.

Après le déploiement, on surveille. Un bon log plus une alerte, c'est la différence entre repérer une intrusion en quelques heures et la repérer en quelques mois. Le délai moyen de détection se compte souvent en mois. Sans journal des événements de sécurité, vous ne saurez même pas que vous avez été visité.

Défense en profondeur :

  1. debug coupé, erreurs génériques à l'écran, détails dans le log ;
  2. logger les événements de sécurité avec contexte, jamais de secret ni de donnée perso brute ;
  3. secrets en environnement (leçon 12), HTTPS + en-têtes (leçon 11) ;
  4. moindre privilège et nettoyage des fichiers oubliés (.git, .env, backups) ;
  5. monitoring + alertes après la mise en ligne.

Référence : OWASP Logging Cheat Sheet.

La méthode et l'arsenal du pentester

Avant de refermer le cours, remettons-nous une dernière fois dans la peau de l'attaquant, pour voir comment il exploite exactement ce qu'on vient de corriger.

1. Faire planter pour faire parler. On envoie des entrées malformées (un type inattendu, un tableau là où on attend une chaîne) pour provoquer une erreur, puis on lit la trace : version, chemins, requête SQL. Une app bavarde se cartographie toute seule.

2. Chercher ce qui traîne. On teste les classiques oubliés : /.git/, /.env, /phpinfo.php, /backup.zip, /admin, les fichiers .bak. C'est du forced browsing (leçon 6) avec une wordlist, et ça paie souvent.

3. Lire les fuites passives. Les en-têtes Server: et X-Powered-By:, les commentaires dans le HTML, les messages d'erreur : autant d'indices gratuits sur la stack et les versions, qu'on croise ensuite avec les CVE.

L'arsenal.

  • Burp Suite / OWASP ZAP : provoquer et lire les erreurs, inspecter les en-têtes, fuzzer les paramètres.
  • ffuf / dirsearch / Nikto : débusquer les fichiers et endpoints oubliés (.git, .env, backups, panneaux d'admin).
  • Les en-têtes de réponse : le fingerprint gratuit de la stack, à lire avant toute autre chose.

La mise en prod : le moment où tout se joue

Cette dernière étape noue tout le cours. Le jour du déploiement, les treize leçons se rejoignent : les injections (leçons 3 à 5), le contrôle d'accès (6), l'authentification et les sessions (7-8), CSRF, CORS et les en-têtes (9 à 11), la cryptographie (12), les dépendances (13), et la discrétion des erreurs et des logs (celle-ci).

Le piège, c'est qu'une seule case oubliée peut annuler tout le reste. Un mode debug resté allumé, un .env accessible, et le plus beau code du monde livre quand même ses secrets. D'où la discipline : on ne déploie pas au jugé, on déroule une checklist, à chaque mise en ligne.

Ce que ce cours vous laisse : la sécurité n'est pas une fonctionnalité qu'on colle à la fin, c'est une manière de coder à chaque ligne. Vous avez maintenant l'œil de l'attaquant, celui qui demande « et si je faisais ça ? », pour coder en défenseur. La suite, c'est la pratique : entraînez-vous sur des plateformes légales (Web Security Academy, Juice Shop), gardez l'OWASP sous la main, et n'oubliez jamais la leçon 1 : on ne teste que ce qu'on a le droit de tester.

La checklist finale de déploiement

Le récapitulatif de tout le cours, à dérouler avant chaque mise en ligne.

  • Debug coupé, erreurs génériques à l'écran, détails dans le log (cette leçon).
  • Secrets en environnement (.env hors web root), jamais dans le code ni dans git (leçon 12).
  • HTTPS + HSTS + CSP et en-têtes de sécurité (leçon 11).
  • Auth solide : bcrypt/Argon2, MFA ; sessions HttpOnly/Secure/SameSite (leçons 7-8).
  • Requêtes paramétrées, encodage en sortie, autorisation serveur, token CSRF (leçons 3-9).
  • Moindre privilège : utilisateur de base limité, droits fichiers, process non-root (leçon 5).
  • Dépendances à jour (audit), lockfile commité (leçon 13).
  • Fichiers oubliés supprimés : .git, .env, phpinfo, sauvegardes, panneaux d'admin.
  • Logs de sécurité (sans secrets) + monitoring + alertes.

Pour aller plus loin. Les OWASP Cheat Sheets couvrent chaque sujet en détail, l'ASVS donne un référentiel de vérification complet, et la Web Security Academy reste la meilleure salle d'entraînement gratuite. Bravo d'être allé au bout : vous codez désormais en pensant comme un attaquant. Et si vous voulez continuer ici même : le Niveau 2 du cours commence par le path traversal, attaquer les chemins de fichiers pour lire ce que le serveur ne devrait jamais montrer, puis l'upload, le SSRF, et plus encore.

Rappel, une dernière fois. Tout ce que ce cours a montré sert à défendre. On ne teste que ses propres systèmes ou une cible explicitement autorisée. Attaquer un système tiers sans autorisation écrite est un délit (leçon 1).

The debug page that hands over the server

A site in production crashes on an error. Instead of a sober message, the visitor lands on a beautiful debug page, the kind frameworks show (Laravel, Symfony, Django): the exception, the full trace with file paths, and, jackpot, the environment variables, including DB_PASSWORD and the application's secret key.

The attacker didn't even have to attack: the site handed them its own map. Database credentials, framework version (so the known flaws, lesson 13), internal structure: it's all there, on screen. Leaving debug mode on in production is the most common web misconfiguration.

This final lesson is about going to production: logging without leaking, handling errors without revealing everything, and the checklist that ties the previous thirteen lessons together. It maps to OWASP 2025 (A02 Security Misconfiguration, A09 Logging & Alerting Failures and A10 Mishandling of Exceptional Conditions).

The three go-live mistakes

1. Saying too much in errors. A detailed trace shows the framework, the paths, the SQL query, sometimes the secrets. It's a map handed to the attacker. In production, the visitor should only see a generic message; the details go to the log, server-side.

2. Logging badly. Two opposite excesses, equally dangerous:

  • Too much: logging passwords, tokens, request bodies, personal data. Logs are often less protected than the database. They are copied to third-party aggregators and read by many people. A secret in a log is a secret that leaks.
  • Too little: logging nothing of logins, auth failures, access denials. You go blind: you can't detect an intrusion, nor investigate afterwards.

3. Debug mode in production. The number-one mistake, the one from the start. Frameworks' debug pages spill the whole environment, secrets included. The rule is absolute: APP_DEBUG=false, display_errors=Off, always, in prod.

Beware "it's just an error message". A verbose error is never cosmetic. It hands over the framework version (so the CVEs to try, lesson 13), the server paths, the database structure, sometimes a whole SQL query. The attacker maps your application with zero effort, just by making it crash.

Your turn to attack: read the secrets in the error

Here's an app about to crash (a database query fails). Set the debug mode, then trigger the error, and look at what the visitor sees. In debug, you'll read the database credentials; in production, just a neutral message. Everything is simulated.

mysite.com · an error is about to happen

        
What just happened

In debug, the error page spills everything: the exception, the file paths, the version, the SQL query, and above all the environment variables with the database password. The attacker just helps themselves. It's the most common misconfiguration, and one of the most serious.

In production, the visitor only sees a neutral message with an incident reference. The details (the exception, the query) are recorded, but in the server log, out of the visitor's reach. You can debug; the attacker learns nothing.

The fix: deploy cleanly

Errors. In production, a generic message on screen, the details in the log:

// production: no technical error on screen
ini_set('display_errors', '0');
// + APP_DEBUG=false (Laravel/Symfony), DEBUG=False (Django)
// → the visitor sees "An error occurred (ref. #a3f1)"
// → the full exception goes to the log, server-side

Logs. You record security events (login, auth failure, access denial, privilege change) with useful context (who, what, when, from where). But you never record a secret, password, token or raw personal data. You redact, centralize, alert, retain.

// GOOD: a useful event, no secret
$log->warning('login_failed', ['user' => $email, 'ip' => $ip, 'at' => now()]);
// NEVER: $log->info('login', ['password' => $password]);  ← the secret leaks into the log

Deployment. Security isn't checked "by feel": you run a list. Debug off, secrets in environment variables (lesson 12), HTTPS and headers (lesson 11), least privilege (lesson 5), dependencies up to date (lesson 13), and you delete what's lying around: exposed .git/, accessible .env, /phpinfo.php, backups, default accounts.

After deployment, you monitor. A good log plus an alert is the difference between spotting an intrusion in hours and spotting it in months. The average detection time is often counted in months. Without a log of security events, you won't even know you've been visited.

Defense in depth:

  1. debug off, generic errors on screen, details in the log;
  2. log security events with context, never a secret or raw personal data;
  3. secrets in environment (lesson 12), HTTPS + headers (lesson 11);
  4. least privilege and cleanup of forgotten files (.git, .env, backups);
  5. monitoring + alerts after going live.

Reference: OWASP Logging Cheat Sheet.

The pentester's method and arsenal

Before we close out the course, let's put ourselves in the attacker's shoes one last time, to see exactly how they exploit what we just fixed.

1. Crash it to make it talk. You send malformed input (an unexpected type, an array where a string is expected) to trigger an error, then read the trace: version, paths, SQL query. A chatty app maps itself.

2. Look for what's lying around. You test the forgotten classics: /.git/, /.env, /phpinfo.php, /backup.zip, /admin, .bak files. It's forced browsing (lesson 6) with a wordlist, and it often pays off.

3. Read the passive leaks. The Server: and X-Powered-By: headers, HTML comments, error messages: free clues about the stack and versions, which you then cross-reference with CVEs.

The arsenal.

  • Burp Suite / OWASP ZAP: trigger and read errors, inspect headers, fuzz parameters.
  • ffuf / dirsearch / Nikto: dig out forgotten files and endpoints (.git, .env, backups, admin panels).
  • The response headers: the free fingerprint of the stack, to read before anything else.

Going to production: where it all comes together

This last step ties the whole course together. On deployment day, the thirteen lessons converge: injections (lessons 3 to 5), access control (6), authentication and sessions (7-8), CSRF, CORS and headers (9 to 11), cryptography (12), dependencies (13), and the discretion of errors and logs (this one).

The trap is that a single forgotten box can undo all the rest. A debug mode left on, an accessible .env, and the finest code in the world still hands over its secrets. Hence the discipline: you don't deploy by guesswork, you run a checklist, on every go-live.

What this course leaves you with: security isn't a feature you bolt on at the end, it's a way of coding on every line. You now have the attacker's eye, the one that asks "what if I did this?", to code like a defender. The next step is practice: train on legal platforms (Web Security Academy, Juice Shop), keep OWASP at hand, and never forget lesson 1: you only test what you're allowed to test.

The final deployment checklist

The recap of the whole course, to run before every go-live.

  • Debug off, generic errors on screen, details in the log (this lesson).
  • Secrets in environment (.env outside the web root), never in the code or in git (lesson 12).
  • HTTPS + HSTS + CSP and security headers (lesson 11).
  • Strong auth: bcrypt/Argon2, MFA; sessions HttpOnly/Secure/SameSite (lessons 7-8).
  • Parameterized queries, output encoding, server-side authorization, CSRF token (lessons 3-9).
  • Least privilege: limited database user, file permissions, non-root process (lesson 5).
  • Dependencies up to date (audit), committed lockfile (lesson 13).
  • Forgotten files deleted: .git, .env, phpinfo, backups, admin panels.
  • Security logs (no secrets) + monitoring + alerts.

To go further. The OWASP Cheat Sheets cover each topic in detail, the ASVS gives a complete verification standard, and the Web Security Academy remains the best free training ground. Well done for going all the way: you now code thinking like an attacker. And if you want to keep going right here: Level 2 of the course starts with path traversal, attacking file paths to read what the server should never show, then upload, SSRF, and more.

Reminder, one last time. Everything this course showed serves to defend. You only test your own systems or an explicitly authorized target. Attacking a third-party system without written authorization is a crime (lesson 1).

Prédisez avant de lire

Un site en production affiche une page de debug détaillée quand une erreur survient. Avant de dérouler : qu'est-ce que l'attaquant apprend, et que devrait montrer la prod à la place ?

Voir la réponse

L'attaquant apprend une carte du serveur : la version du framework (donc les failles connues à essayer), les chemins des fichiers, la structure de la base, parfois la requête SQL et les variables d'environnement avec les mots de passe. Il n'a même pas eu à attaquer. En production, le visiteur ne devrait voir qu'un message générique (« une erreur est survenue », avec une référence d'incident) ; les détails techniques doivent partir dans le log serveur, jamais à l'écran. La règle : APP_DEBUG=false en prod.

Predict before reading on

A site in production shows a detailed debug page when an error occurs. Before you expand: what does the attacker learn, and what should production show instead?

Show the answer

The attacker learns a map of the server: the framework version (so the known flaws to try), the file paths, the database structure, sometimes the SQL query and the environment variables with the passwords. They didn't even have to attack. In production, the visitor should only see a generic message ("an error occurred", with an incident reference); the technical details should go to the server log, never on screen. The rule: APP_DEBUG=false in prod.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

💬 Ré-explique sans regarder
Ré-explique sans regarder

Avec tes mots : pourquoi une erreur verbeuse et un mauvais logging aident l'attaquant, et que doit faire une app en production pour les erreurs et les logs ?

Une bonne explication dit : une erreur verbeuse (une trace, une page de debug) donne à l'attaquant une carte gratuite : version du framework (donc les CVE à essayer), chemins, requête SQL, parfois les secrets de l'environnement. Et un mauvais logging fait fuiter dans deux sens : trop logger (mots de passe, tokens, données perso dans des logs souvent peu protégés et partagés) expose des secrets ; trop peu logger (rien sur les connexions, les échecs, les refus d'accès) rend aveugle, incapable de détecter une intrusion. En production, l'app doit : afficher un message générique à l'écran et envoyer les détails dans le log serveur ; couper le mode debug (APP_DEBUG=false) ; logger les événements de sécurité avec du contexte mais jamais de secret ; puis surveiller et alerter.
🧠 Rappel libre
Rappel libre

Sans remonter : cite les trois fautes de la mise en prod (erreurs, logs, debug), et donne trois lignes de la checklist de déploiement.

Trois fautes : (1) des erreurs trop verbeuses à l'écran (trace, version, requête SQL, secrets) ; (2) un mauvais logging : trop (secrets et données perso dans les logs) ou trop peu (aucun événement de sécurité, donc aveugle) ; (3) le mode debug laissé en production. Checklist (extraits) : debug coupé et erreurs génériques (détails au log) ; secrets en variables d'environnement, hors du code et de git ; HTTPS + en-têtes de sécurité ; moindre privilège ; dépendances à jour ; supprimer les fichiers oubliés (.git, .env, backups) ; logs de sécurité sans secrets + alertes.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

Des connexions échouent et tu n'arrives pas à comprendre pourquoi. L'IA propose : « Pour débugger, je logge la requête de connexion complète, avec l'email et le mot de passe saisi. Comme ça tu vois exactement ce qui est envoyé. On enlèvera après. » Tu acceptes, ou tu rejettes ?

À rejeter, sans hésiter. On ne logge jamais un mot de passe (ni un token, ni un numéro de carte). Les logs sont souvent moins protégés que la base : copiés vers des agrégateurs tiers, lus par l'équipe, conservés des mois, parfois exposés par accident. Un mot de passe en clair dans un log, c'est une fuite garantie, et le « on enlèvera après » se transforme presque toujours en oubli. Pour débugger une connexion, on logge ce qui est utile sans le secret : l'email, l'IP, l'horodatage, le résultat (succès / échec / compte inconnu), éventuellement la longueur du mot de passe, jamais sa valeur. Et si on a vraiment besoin du détail, ça reste en local, jamais en prod.
En production, une erreur affiche la trace complète avec les identifiants de la base. Quel est le problème ?
Pour débugger une connexion qui échoue, faut-il logger le mot de passe saisi ?
Quelle est la mauvaise configuration la plus courante en mise en production ?
À quoi sert un bon journal des événements de sécurité, une fois en production ?
Prochaine étape

Bravo : vous avez bouclé les 14 leçons du Top 10. Envie d'aller plus loin ? Le Niveau 2 ajoute six attaques spécifiques, chacune avec son labo : path traversal, upload, SSRF, injection de template, désérialisation, et la sécurité des applications IA.

Niveau 2 : Path traversal →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement