Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

12.1 — OWASP Top 10

Confirmé 35 min prérequis : axes 8 (backend) et 11 (qualité) lus

🎯 Objectif : connaître les 10 catégories de vulnérabilités OWASP. Si tu peux les nommer, tu peux les chercher dans ton code.

À l'issue de cet axe, tu sauras :

  • Connaître les 10 catégories OWASP édition 2021 (toujours référence en 2026)
  • Pour chacune : exemple concret, impact, prévention
  • Reconnaître les vulnérabilités dans du code existant
  • Choisir les bons outils pour scanner automatiquement

💡 Termes glossaire rencontrés dans cette section : xss , csrf , sqli , cors , jwt , argon2id , rgpd .

L’OWASP Foundation (Open Web Application Security Project) publie tous les ~3 ans un classement des vulnérabilités web les plus critiques. C’est la référence mondiale.

Le Top 10 actuel date de 2021 ; la 2025/2026 est en préparation mais les catégories restent largement valables.

La règle d’or : tu ne dois pas mémoriser tous les détails. Tu dois pouvoir dire le nom d’une vulnérabilité en lisant du code suspect, et savoir où chercher la solution.

Problème : un utilisateur accède à des ressources qui ne lui appartiennent pas.

// ❌ IDOR (Insecure Direct Object Reference)
app.get('/orders/:id', async (req, res) => {
const order = await db.order.findById(req.params.id);
res.json(order); // PAS de vérif que c'est bien l'order du user connecté !
});

→ Un user peut accéder à /orders/42 même si ce n’est pas le sien.

  • Toujours vérifier que la ressource appartient à l’utilisateur connecté.
  • Utiliser le Row-Level Security (Postgres / Supabase) pour défense en profondeur.
  • Logger les accès refusés pour détecter les tentatives.
// ✅
app.get('/orders/:id', async (req, res) => {
const order = await db.order.findById(req.params.id);
if (!order || order.userId !== req.user.id) {
return res.status(404).json({ error: 'Not found' }); // 404, pas 403 (ne révèle pas l'existence)
}
res.json(order);
});

Problème : données sensibles non chiffrées ou mal chiffrées.

  • Mots de passe stockés en clair ou en MD5/SHA-256.
  • Données sensibles transmises en HTTP.
  • Clés API hardcodées dans le code source.
  • TLS désactivé en prod.
  • HTTPS partout (HSTS, redirect HTTP → HTTPS).
  • Hash mots de passe : argon2id ou bcrypt — JAMAIS MD5 ou SHA-256.
  • Secrets dans un secret manager, pas en repo.
  • TLS 1.3 minimum.
  • Données sensibles chiffrées au repos (DB, backups).

Problème : input utilisateur interprété comme du code/commande.

// ❌
const sql = `SELECT * FROM users WHERE email = '${email}'`;
db.query(sql);
// Si email = "alice' OR 1=1 --" → tous les users !
// ❌
db.users.find({ email: req.body.email, password: req.body.password });
// Si body = { email: "...", password: { $ne: null } } → bypass auth !
// ❌
exec(`convert ${req.body.filename} output.png`);
// Si filename = "input.jpg; rm -rf /" → catastrophe
  • Requêtes paramétrées : db.query('... WHERE email = $1', [email]).
  • ORMs (Prisma, Drizzle, Eloquent, Doctrine) qui paramétrisent automatiquement.
  • Validation stricte côté serveur (Zod, Pydantic).
  • Whitelist plutôt que blacklist.
  • Pour les commandes shell : utiliser des libs spécifiques (execFile, spawn avec args array, jamais exec avec string concaténée).

Problème : faille architecturale, pas un bug d’implémentation.

  • Un endpoint /reset-password qui n’a pas de rate-limit → brute-force.
  • Un système de récupération de mot de passe basé sur la question secrète.
  • Une fonctionnalité « inviter par email » qui envoie sans modération → spam abuse.
  • Un panier dont le prix est calculé côté client et envoyé au serveur.
  • Threat modeling au moment de la conception (axe 3).
  • Zero trust : ne fais confiance à aucune valeur venant du client.
  • Rate-limit systématique sur les endpoints sensibles.
  • Penser abuse cases, pas juste use cases.

Problème : config par défaut non sécurisée, headers manquants, debug mode en prod.

  • DEBUG=true en prod → stack traces exposées.
  • Page d’admin accessible sans mot de passe.
  • En-têtes de sécurité absents (X-Frame-Options, Content-Security-Policy).
  • CORS * en prod sur API privée.
  • Bucket S3 public.

Problème : tu utilises une lib avec une CVE connue.

"dependencies": {
"lodash": "4.17.10" // CVE-2019-10744 — prototype pollution
}

Tu n’as rien fait de mal toi, mais un attaquant exploite la lib.

  • Dependabot (GitHub) ou Renovate : PR automatiques pour les mises à jour de sécurité.
  • npm audit / pnpm audit régulier.
  • Snyk ou socket.dev : scan plus poussé que npm audit.
  • Lock-file commit : versions exactes en prod.
  • Supprimer les deps non utilisées (chaque dep est une surface d’attaque).

A07 — Identification and Authentication Failures

Section intitulée « A07 — Identification and Authentication Failures »

Problème : auth mal implémentée.

  • Mot de passe 12345 accepté.
  • Pas de protection brute-force sur /login.
  • Session token prévisible (incrémental, basé sur timestamp).
  • Token JWT sans expiration.
  • Pas de logout serveur (cookie non révoqué).
  • Politique de mots de passe : minimum 12 caractères, vérifier contre HaveIBeenPwned.
  • MFA ou Passkeys disponibles.
  • Rate-limit + lockout après N tentatives.
  • Tokens cryptographiquement aléatoires (UUID v4, jamais incrémental).
  • Expiration + rotation des tokens.
  • Utiliser des librairies éprouvées : Auth.js, Clerk, Auth0, Supabase Auth — pas une auth maison.

Problème : code ou données chargés sans vérification d’intégrité.

  • CDN externe sans Subresource Integrity (SRI).
  • Pipeline CI qui télécharge des binaires sans vérifier le hash.
  • Updates auto sans signature.
  • Désérialisation non sûre (PHP unserialize, Java ObjectInputStream).
<!-- SRI -->
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-Xxx..."
crossorigin="anonymous">
</script>
  • Vérifier les hashs des fichiers téléchargés en CI.
  • Signer les artefacts (Sigstore, cosign).
  • Pinner les versions des actions GitHub par SHA, pas par tag.

Problème : tu ne sais pas qu’on t’attaque.

  • Pas de log des login échoués → tu ne vois pas le brute-force.
  • Logs non centralisés → impossible de corréler.
  • Logs avec mots de passe en clair ou tokens.
  • Aucune alerte automatique sur événement anormal.
  • Logs structurés (JSON) centralisés (ELK, Loki, Datadog, Sentry).
  • Logger : login OK/KO, accès admin, modif de droits, exports massifs.
  • Pas de données sensibles dans les logs (caviarder mot de passe, token, CB).
  • Alerting sur patterns : 100 logins KO depuis une IP → bloquer.

Problème : ton serveur fait des requêtes à des URLs fournies par l’utilisateur, qui peuvent être internes.

// ❌ Génération de PDF depuis une URL fournie
app.get('/pdf', async (req, res) => {
const html = await fetch(req.query.url).then(r => r.text());
// Si url = http://169.254.169.254/latest/meta-data → fuites credentials AWS !
// Si url = http://localhost:5432 → exfiltration DB locale
});
  • Whitelist des domaines autorisés.
  • Bloquer les IP privées (10.x, 192.168.x, 172.16-31.x, 127.x, 169.254.x).
  • Lib dédiée : ssrf-req-filter (Node), équivalents par langage.
  • Ne pas suivre les redirects automatiquement vers des IPs internes.
#CatégorieMot-cléOutil principal
A01Broken Access Control« Cet user ne devrait pas voir ça »Tests E2E + RLS
A02Cryptographic Failures« Stocker en clair, MD5 »argon2id, HTTPS, secret manager
A03Injection« Input concaténé en SQL/shell »ORM, parameterized queries
A04Insecure Design« Faille architecturale »Threat modeling
A05Misconfiguration« DEBUG=true en prod »Security headers, audit
A06Vulnerable Components« lodash 4.17.10 »Dependabot, Snyk
A07Auth Failures« Brute-force ouvert »Rate-limit, MFA, libs éprouvées
A08Integrity Failures« SRI absent »Hashs, signatures
A09Logging Failures« Pas d’alerte sur attaque »Logs structurés, Sentry
A10SSRF« URL utilisateur fetchée »Whitelist, bloquer IPs internes
OutilRôle
SnykScan dépendances + IaC + code (commercial, free tier)
DependabotPR auto pour deps vulnérables (GitHub)
SemgrepAnalyse statique de patterns dangereux
OWASP ZAPPentest automatique
Burp SuitePentest manuel pro
gitleaks / trufflehogDétection secrets dans le code
TrivyScan de containers et IaC
Tu vois `db.query(\`SELECT * FROM orders WHERE id = ${req.params.id}\`)`. Quelle catégorie OWASP ?
Tu envoies un email de reset de password mais tu n'as PAS de rate-limit sur le endpoint. Quelle catégorie OWASP ?
Tu utilises une lib `lodash@4.17.10` qui a la CVE-2019-10744 (prototype pollution). Quelle catégorie ?

Suite : 12.2 — Sécurité frontend — XSS, CSRF, CSP.