2026-11-04 — Design, authentification et robustesse
Contexte
Matière : _Test d’intrusion web · Type : cours (Jour 3/3) · Source : support ESIEA_Audit_Web_Jour3 Précédent : 2026-11-03 — Dépendances, crypto et injection
🎯 Objectifs de la séance
- Repérer les défauts de conception (A06) : logique métier, conditions de course, absence de limites.
- Exploiter et corriger les défaillances d’authentification (A07) : brute force, énumération, JWT, sessions, reset, MFA.
- Comprendre l’intégrité (A08), la journalisation (A09) et la gestion des erreurs (A10).
Examen
Vendredi 13 novembre, sur Moodle.
Partie 1 — A06 Insecure Design
Définition
La faille est dans la conception, pas dans un bug ponctuel : aucun correctif de ligne ne la règle. Correction : modéliser les abus dès la conception (threat modeling) et tout vérifier côté serveur.
- Logique métier : sauter une étape, rejouer une transition, contourner un paiement.
- Conditions de course (TOCTOU, double dépense) : requêtes concurrentes.
- Absence de limites : pas de rate limiting, quotas non vérifiés.
- Falsification de paramètres métier : prix, quantités, montants négatifs.
Contournement de logique métier
L’application suppose que l’utilisateur suit le parcours prévu. On casse cette hypothèse :
- passer directement à
/checkout/confirmsans payer ; - appliquer deux fois un code promo ; quantité ou prix négatif ;
- omettre un paramètre qui déclenche une branche « par défaut » permissive ;
- rejouer un webhook ou une transition déjà effectuée.
Conditions de course & absence de limites
Deux requêtes simultanées peuvent passer un contrôle avant que l’état ne soit mis à jour (double dépense). Sans rate limiting, brute force et abus deviennent triviaux.
// 💥 Vérifier puis agir (TOCTOU) : 2 requêtes en parallèle passent toutes deux le if
if (account.balance >= amount) {
account.balance -= amount
payout(amount)
}-- 🛡 Opération atomique en base
UPDATE accounts
SET balance = balance - :amt
WHERE id = :id AND balance >= :amt;
-- + verrou / clé d'idempotence + rate limiting par compte / IPRègle
Rendre l’opération atomique en base + verrou / clé d’idempotence, et limiter le débit.
Partie 2 — A07 Authentication Failures
Définition
Tout ce qui permet de se faire passer pour quelqu’un d’autre : prouver qui l’on est est mal implémenté.
- Brute force & credential stuffing sans limite ni verrouillage.
- Énumération d’utilisateurs : réponses différentes selon que le compte existe.
- JWT mal vérifiés : algorithme
none, secret faible, confusion de clés. - Sessions : fixation, absence d’invalidation, cookies non sécurisés.
- Réinitialisation de mot de passe : jetons faibles, poisoning via l’en-tête
Host. - MFA contournable : OTP sans limite, étape sautable.
Attaques sur les JWT
Un JWT n’est de confiance que si sa signature est vérifiée correctement. Deux classiques :
- accepter
alg: none(jeton non signé) ; - casser un secret HMAC faible hors ligne, puis forger ses propres jetons.
// 💥 Vérification permissive : "none" accepté
jwt.verify(token, key, { algorithms: ["HS256", "none"] })
// ou clé HMAC triviale ("secret") → forgeable hors ligne
// 🛡 Algorithme figé + clé longue et aléatoire
jwt.verify(token, key, { algorithms: ["HS256"] })
// ou RS256 : la clé publique sert uniquement à vérifierRègle
Figer l’algorithme attendu et une clé forte (ou RS256) ; vérifier la signature systématiquement.
Brute force, sessions & réinitialisation
| Sujet | 🛡 Bonne pratique |
|---|---|
| Brute force / énumération | limiter les tentatives, verrouillage, réponses et temps de réponse identiques (login & reset) |
| Fixation de session | régénérer l’identifiant de session après connexion ; invalider à la déconnexion |
| Cookies de session | HttpOnly, Secure, SameSite ; durée de vie limitée ; expiration d’inactivité |
| Reset de mot de passe | jeton long, aléatoire, à usage unique et expirant ; ne pas se fier à l’en-tête Host |
| MFA | limiter les OTP, empêcher de sauter l’étape, vérifier sur tous les endpoints |
Partie 3 — A08 Software or Data Integrity Failures
Définition
Faire confiance à des données ou du code sans vérifier leur intégrité : désérialisation non sûre, mises à jour non signées, intégrité CI/CD.
Désérialisation non sûre
Désérialiser des données contrôlées par l’attaquant peut instancier des objets arbitraires et déclencher du code pendant la reconstruction (gadget chains), souvent jusqu’au RCE.
# 💥 pickle : un objet forgé exécute du code via __reduce__
import pickle
obj = pickle.loads(request.get_data())
# 🛡 JSON = format de données, pas de code
import json
obj = json.loads(request.get_data())
# si un format binaire s'impose : signer (HMAC) et vérifier, restreindre les classes permisesRègle
Ne jamais désérialiser de données non fiables en format exécutable : préférer JSON, sinon signer et vérifier.
Intégrité du code, des mises à jour & du pipeline
- 💥 Mises à jour / plugins installés sans vérification de signature ; artefacts de build empoisonnés, cache de pipeline altéré, images non signées ; objets sérialisés acceptés sans contrôle.
- 🛡 Signer et vérifier binaires, mises à jour et artefacts (Sigstore, signatures) ; verrouiller les dépendances ; épingler les actions CI au SHA.
Partie 4 — A09 Security Logging & Alerting Failures
Définition
Sans journalisation ni alerte fiables, une attaque passe inaperçue, et les journaux eux-mêmes peuvent être falsifiés.
Défauts fréquents
- Événements de sécurité non journalisés (échecs d’auth, accès refusés).
- Pas d’alerte, pas de corrélation, pas de rétention.
- Données sensibles en clair dans les logs.
- Logs falsifiables : injection de fausses lignes.
# 💥 Log injection : user = "admin\n[INFO] ok" forge une fausse ligne
log.info("login: " + user)
# 🛡 Logs structurés (JSON), \r \n échappés
log.info("login", extra={"user": user})
# + ne jamais journaliser mots de passe ni jetonsPartie 5 — A10 Mishandling of Exceptional Conditions
Définition (nouveauté 2025)
Quand une erreur survient, l’application doit échouer de façon sûre (fail-closed), sans trop en dire, et sans s’effondrer sous une entrée malveillante.
- Logique fail-open : une erreur accorde l’accès.
- Fuites via messages d’erreur.
- Exceptions non gérées, épuisement de ressources.
// 💥 Fail-open : une erreur → accès accordé
try { allowed = checkAccess(user) }
catch (e) { allowed = true }
// 🛡 Fail-closed
try { allowed = checkAccess(user) }
catch (e) { allowed = false; log.error(e) }
// + message générique à l'utilisateur + limites de taille / timeoutsRègle
Échouer en mode fermé (refus par défaut), journaliser l’erreur, message générique à l’utilisateur.
Récapitulatif OWASP Top 10:2025
| Catégories | Ce qu’on retient |
|---|---|
| A01–A02 | vérifier l’accès côté serveur ; durcir la configuration |
| A03–A04 | maîtriser dépendances & chaîne CI/CD ; ne jamais bricoler la crypto |
| A05 | séparer données et code (paramétrage, échappement, pas de shell) |
| A06–A07 | concevoir contre l’abus ; authentifier et gérer les sessions correctement |
| A08–A10 | vérifier l’intégrité ; journaliser ; échouer de façon sûre |
Fil rouge
Ne jamais faire confiance à une entrée contrôlée par le client.
🧪 Labs du jour
Voir Labs PortSwigger — Test d’intrusion web (A06 : 3 labs ; A07 : 5 labs ; A08 : 2 labs ; A10 reprend Information disclosure in error messages).