Injection SQL, contrôle d’accès cassé, dépendances piégées… L’OWASP Top 10 est la liste de référence des failles web les plus fréquentes. Dans ce guide, tu découvres les 10 catégories de l’édition 2025, utilisée en 2026, avec pour chacune un exemple concret, le code vulnérable, le code corrigé et une checklist pour protéger ton application.

🛡️ Sécurité web🔟 10 catégories🐘 PHP & JavaScript✅ Checklist⏱️ 20 min de lecture

L’OWASP Top 10 est le document que tout développeur web devrait connaître par cœur. Publié par l’OWASP (Open Worldwide Application Security Project), une fondation à but non lucratif, il classe les risques de sécurité les plus critiques des applications web à partir de données réelles : des centaines de milliers de failles analysées dans des applications du monde entier.

OWASP Top 10 : Les 10 failles web les plus courantes en 2026 (OWASP Top 10 expliqué)
Les 10 failles web les plus courantes en 2026 (OWASP Top 10 expliqué)

Pourquoi c’est important pour toi ? Parce que la grande majorité des piratages de sites ne viennent pas d’attaques sophistiquées, mais de failles web classiques : un formulaire mal filtré, une page d’administration accessible sans contrôle, une librairie jamais mise à jour. Ce sont exactement celles que l’OWASP Top 10 décrit.

OWASP Top 10 : les 10 catégories de l’édition 2025
Les 10 catégories de l’OWASP Top 10 édition 2025, la référence en 2026.
🎯
Ce que tu vas apprendre
  • Ce qu’est l’OWASP Top 10 et ce qui a changé dans l’édition 2025.
  • Les 10 failles web expliquées simplement, avec un exemple réel pour chacune.
  • Du code vulnérable et sa version corrigée, en PHP et en JavaScript.
  • Une checklist finale à appliquer sur tes projets.

Qu’est-ce que l’OWASP Top 10 ?

L’OWASP Top 10 est un document de sensibilisation. Il ne liste pas des failles précises (comme une CVE), mais des catégories de risques. Chaque catégorie regroupe plusieurs faiblesses techniques, appelées CWE (Common Weakness Enumeration). Par exemple, la catégorie « Injection » regroupe l’injection SQL, l’injection de commandes système, le XSS, etc.

La liste est mise à jour tous les trois à quatre ans. L’édition actuelle, la huitième, est celle de 2025. Pour la construire, l’OWASP a analysé plus de 175 000 enregistrements CVE et 589 CWE (contre environ 400 en 2021), complétés par un sondage auprès de la communauté de la sécurité. C’est donc la version de référence pour sécuriser une application en 2026.

OWASP Top 10 2021OWASP Top 10 2025
Nombre de CWE analysées~400589
Nouvelles catégories32 (chaîne d’approvisionnement, conditions exceptionnelles)
SSRFCatégorie à part (A10)Intégrée au contrôle d’accès (A01)
N°1Contrôle d’accès défaillantContrôle d’accès défaillant
💡
OWASP Top 10, ASVS, Cheat Sheets : ne confonds pas

L’OWASP publie aussi l’ASVS (une liste de contrôles de sécurité très détaillée, idéale pour un audit) et les Cheat Sheets (des fiches pratiques par sujet). Le Top 10 est la porte d’entrée : il te dit quoi surveiller. Les Cheat Sheets te disent comment le corriger.

Les 10 failles web de l’OWASP Top 10 2025 en un coup d’œil

RangCatégorieEn une phrase
A01Contrôle d’accès défaillantUn utilisateur accède à des données ou actions qui ne lui sont pas destinées
A02Mauvaise configuration de sécuritéRéglages par défaut, mode debug, en-têtes manquants
A03Défaillances de la chaîne d’approvisionnementDépendances, outils de build ou paquets compromis
A04Défaillances cryptographiquesDonnées sensibles mal chiffrées ou mots de passe mal stockés
A05InjectionUne donnée utilisateur est exécutée comme du code (SQL, commande, HTML)
A06Conception non sécuriséeLa faille est dans la logique même de l’application
A07Défaillances d’authentificationConnexion contournable, mots de passe faibles, sessions mal gérées
A08Défaillances d’intégrité des logiciels et des donnéesMises à jour ou données acceptées sans vérification
A09Défaillances de journalisation et d’alerteUne attaque passe inaperçue faute de logs et d’alertes
A10Mauvaise gestion des conditions exceptionnellesErreurs mal gérées, application qui « échoue en restant ouverte »

Passons maintenant chacune de ces failles web en revue, avec un exemple concret et la bonne façon de s’en protéger.

A01 : le contrôle d’accès défaillant

C’est la faille web numéro 1, et ce n’est pas un hasard : elle est facile à introduire et difficile à détecter avec des outils automatiques. Le principe : l’application vérifie que tu es connecté, mais pas que tu as le droit d’accéder à la ressource demandée.

L’exemple classique est l’IDOR (Insecure Direct Object Reference). Ta facture est à l’adresse /facture.php?id=1042. Que se passe-t-il si tu remplaces 1042 par 1043 ?

<?php
// ❌ Vulnérable : on vérifie seulement que l'utilisateur est connecté
session_start();
if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    exit('Non connecté');
}
$stmt = $pdo->prepare('SELECT * FROM factures WHERE id = ?');
$stmt->execute([$_GET['id']]);
$facture = $stmt->fetch();
PHP

N’importe quel client connecté peut lire les factures de tous les autres. La correction tient en une condition : la ressource doit appartenir à l’utilisateur.

<?php
// ✅ Corrigé : la facture doit appartenir à l'utilisateur connecté
$stmt = $pdo->prepare('SELECT * FROM factures WHERE id = ? AND client_id = ?');
$stmt->execute([$_GET['id'], $_SESSION['user_id']]);
$facture = $stmt->fetch();
if (!$facture) {
    http_response_code(404); // 404 plutôt que 403 : on ne confirme pas l'existence
    exit('Facture introuvable');
}
PHP

Depuis 2025, cette catégorie inclut aussi le SSRF (Server-Side Request Forgery) : ton serveur va chercher une URL fournie par l’utilisateur (aperçu de lien, import d’image…) et un attaquant lui fait appeler une adresse interne, comme une base de données ou une interface d’administration non exposée.

✅
Les bons réflexes contre A01
  • Refuser par défaut : tout est interdit sauf ce qui est explicitement autorisé.
  • Vérifier les droits côté serveur, à chaque requête. Cacher un bouton dans l’interface ne protège rien.
  • Centraliser les contrôles d’accès dans une fonction ou un middleware, au lieu de les répéter dans chaque page.
  • Pour les fonctions qui appellent des URL externes, n’autoriser qu’une liste blanche de domaines.

A02 : la mauvaise configuration de sécurité

Cette faille web est passée de la 5e à la 2e place. Elle regroupe tout ce qui relève de la configuration plutôt que du code : un serveur laissé avec ses réglages par défaut, une page d’erreur qui affiche la trace complète d’une exception, un dossier listable, un compte administrateur avec le mot de passe d’origine.

Quelques exemples qu’on retrouve très souvent en audit :

  • display_errors = On en production : chaque erreur PHP révèle des chemins de fichiers et parfois des requêtes SQL.
  • Un fichier .env ou .git/ accessible depuis le navigateur.
  • Des services de développement (phpMyAdmin, Adminer, Mailpit) exposés sur Internet.
  • L’absence d’en-têtes de sécurité HTTP.
; php.ini de production : les réglages essentiels
display_errors = Off
log_errors = On
expose_php = Off
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Lax
INI

Côté serveur web, ajoute les en-têtes qui ferment des familles entières d’attaques :

# .htaccess (Apache) : en-têtes de sécurité recommandés
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Content-Security-Policy "default-src 'self'"
Apache
⚠️
Teste ta Content-Security-Policy avant de l’activer

Une CSP trop stricte peut casser ton site (scripts externes, polices, analytics). Commence avec l’en-tête Content-Security-Policy-Report-Only, observe les violations dans la console du navigateur, puis passe en mode bloquant.

A03 : les défaillances de la chaîne d’approvisionnement logicielle

C’est la grande nouveauté de l’édition 2025. Elle élargit l’ancienne catégorie « composants vulnérables et obsolètes » à toute la chaîne qui produit ton application : les paquets npm ou Composer, les images Docker, les outils de build, les extensions de ton IDE, ton pipeline CI/CD.

Cette catégorie a le moins d’occurrences dans les données de test, mais les scores d’exploitation et d’impact les plus élevés. Logique : quand un paquet populaire est compromis, ce sont des milliers d’applications touchées d’un coup. Les attaques par paquets piégés (typosquatting, comme expres au lieu de express) ou par prise de contrôle du compte d’un mainteneur se sont multipliées ces dernières années.

# Auditer les dépendances de ton projet
npm audit              # projets Node.js
composer audit         # projets PHP (Composer 2.4+)
pip-audit              # projets Python (pip install pip-audit)

# Installer exactement les versions verrouillées (CI/CD, production)
npm ci
composer install --no-dev
Bash
✅
Les bons réflexes contre A03
  • Commite toujours le fichier de verrouillage (package-lock.json, composer.lock).
  • Avant d’ajouter une dépendance, vérifie qu’elle est maintenue (dernière version, nombre de mainteneurs, téléchargements).
  • Active les alertes de dépendances de GitHub (Dependabot) ou un équivalent.
  • Moins de dépendances, c’est moins de surface d’attaque : une fonction de 10 lignes ne mérite pas un paquet.

A04 : les défaillances cryptographiques

Cette catégorie couvre les données sensibles mal protégées : transmises en HTTP, stockées en clair, ou « chiffrées » avec des algorithmes dépassés. Le cas le plus fréquent reste le stockage des mots de passe.

<?php
// ❌ Jamais : MD5 et SHA-1 se cassent en quelques secondes avec des tables précalculées
$hash = md5($password);

// ✅ Toujours : password_hash utilise un algorithme lent et salé (bcrypt ou Argon2)
$hash = password_hash($password, PASSWORD_DEFAULT);

// Vérification lors de la connexion
if (password_verify($saisie, $hashEnBase)) {
    // connexion réussie
}
PHP

En JavaScript côté serveur, le principe est le même avec bcrypt ou argon2 :

import bcrypt from 'bcryptjs';

const hash = await bcrypt.hash(motDePasse, 12); // 12 = coût, plus c'est haut, plus c'est lent
const valide = await bcrypt.compare(saisie, hash);
JavaScript
💡
Pourquoi un algorithme « lent » ?

Pour un utilisateur légitime, attendre 100 ms à la connexion est invisible. Pour un attaquant qui teste des milliards de combinaisons sur une base volée, c’est un mur. MD5 calcule des milliards d’empreintes par seconde sur une carte graphique ; bcrypt, quelques milliers.

Autres points à vérifier : HTTPS partout (avec HSTS), aucune clé ou mot de passe dans le code source, et des cookies de session marqués Secure et HttpOnly.

A05 : l’injection

L’injection descend à la 5e place, mais reste l’une des failles web les plus dangereuses. Elle se produit quand une donnée fournie par l’utilisateur est interprétée comme du code : une requête SQL, une commande système ou du HTML.

L’injection SQL

<?php
// ❌ Vulnérable : la saisie est collée dans la requête
$sql = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";

// ✅ Corrigé : requête préparée, la saisie est toujours traitée comme une donnée
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
PHP

Avec la requête préparée, même une saisie comme ' OR '1'='1 est cherchée littéralement comme une adresse e-mail. Elle ne peut plus modifier la structure de la requête.

Le XSS (Cross-Site Scripting)

Le XSS est une injection dans le HTML : l’attaquant fait exécuter son JavaScript dans le navigateur des autres visiteurs, par exemple via un commentaire.

<?php
// ❌ Vulnérable : le commentaire est affiché tel quel
echo '<p>' . $commentaire . '</p>';

// ✅ Corrigé : les caractères spéciaux HTML sont échappés
echo '<p>' . htmlspecialchars($commentaire, ENT_QUOTES, 'UTF-8') . '</p>';
PHP
// ❌ Vulnérable en JavaScript
element.innerHTML = commentaire;

// ✅ Corrigé : textContent n'interprète jamais le HTML
element.textContent = commentaire;
JavaScript
🛡️
La règle d’or contre l’injection

Ne fais jamais confiance aux données qui viennent de l’extérieur : formulaires, URL, cookies, en-têtes, fichiers importés, réponses d’API. Valide à l’entrée (type, longueur, format) et échappe à la sortie, selon le contexte (SQL, HTML, attribut, URL, commande).

A06 : la conception non sécurisée

Ici, le code peut être parfaitement écrit : c’est la logique qui est fausse. Aucun outil automatique ne détectera qu’un code promo peut être appliqué dix fois, qu’une réinitialisation de mot de passe repose sur une question secrète devinable, ou qu’un panier accepte une quantité négative.

Exemple réel de conception non sécurisée : un formulaire « mot de passe oublié » qui répond « Aucun compte avec cette adresse » ou « E-mail envoyé ». Un attaquant peut ainsi tester une liste d’adresses et savoir lesquelles ont un compte (énumération).

<?php
// ✅ Même réponse dans tous les cas : impossible de savoir si le compte existe
envoyerLienSiCompteExiste($email);
echo 'Si un compte existe pour cette adresse, un e-mail vient d\'être envoyé.';
PHP
✅
Les bons réflexes contre A06
  • Pour chaque fonctionnalité, pose-toi la question : « Comment un utilisateur malveillant pourrait-il en abuser ? »
  • Limite le nombre de tentatives (connexion, codes promo, envoi d’e-mails).
  • Vérifie toujours les montants et quantités côté serveur, jamais seulement en JavaScript.

A07 : les défaillances d’authentification

Cette catégorie regroupe tout ce qui permet de se connecter à la place de quelqu’un d’autre : absence de limite de tentatives (attaque par force brute), acceptation de mots de passe faibles ou déjà divulgués, sessions qui n’expirent jamais, identifiant de session non renouvelé après la connexion.

<?php
// Après une connexion réussie : nouvel identifiant de session
// pour empêcher la fixation de session
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
PHP
✅
Les bons réflexes contre A07
  • Active la double authentification (2FA), au minimum pour les comptes administrateurs.
  • Limite les tentatives de connexion (par exemple 5 essais, puis délai croissant).
  • Refuse les mots de passe connus comme compromis (l’API gratuite Have I Been Pwned permet de les vérifier sans envoyer le mot de passe).
  • Ne réinvente pas l’authentification : utilise celle de ton framework (Laravel, Symfony, Django, Passport.js…).

A08 : les défaillances d’intégrité des logiciels et des données

L’application fait confiance à quelque chose qu’elle n’a pas vérifié : une mise à jour téléchargée sans contrôle de signature, un script chargé depuis un CDN qui pourrait être modifié, ou des données sérialisées reçues de l’utilisateur.

Pour les scripts externes, l’attribut integrity (Subresource Integrity) garantit que le fichier chargé est exactement celui que tu as validé :

<!-- L'empreinte "integrity" est fournie par le CDN (bouton « Copy SRI » sur cdnjs) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"
        integrity="sha512-EMPREINTE_FOURNIE_PAR_LE_CDN"
        crossorigin="anonymous"></script>
HTML

Si le fichier du CDN est modifié, même d’un seul caractère, le navigateur refuse de l’exécuter.

⚠️
Attention à unserialize()

En PHP, n’appelle jamais unserialize() sur une donnée qui vient de l’utilisateur (cookie, champ caché) : elle peut instancier des objets arbitraires. Utilise json_decode(), qui ne crée que des tableaux et des valeurs simples.

A09 : les défaillances de journalisation et d’alerte

Sans journaux, une attaque peut durer des mois sans que personne ne s’en aperçoive. L’édition 2025 insiste sur le mot alerte : enregistrer les événements ne suffit pas, il faut que quelqu’un soit prévenu quand quelque chose d’anormal se produit.

Les événements à journaliser au minimum :

  • les connexions réussies et échouées, avec l’adresse IP et la date ;
  • les changements de mot de passe, d’e-mail ou de droits ;
  • les erreurs d’accès refusé (A01) répétées ;
  • les actions sensibles (suppression, export de données, paiement).
<?php
// Exemple simple de journal de sécurité (sans jamais enregistrer le mot de passe !)
function journalSecurite(string $evenement, array $contexte = []): void
{
    $ligne = sprintf(
        "[%s] %s ip=%s %s\n",
        date('c'),
        $evenement,
        $_SERVER['REMOTE_ADDR'] ?? '-',
        json_encode($contexte, JSON_UNESCAPED_UNICODE)
    );
    file_put_contents(__DIR__ . '/../logs/securite.log', $ligne, FILE_APPEND | LOCK_EX);
}

journalSecurite('connexion_echouee', ['email' => $email]);
PHP
🚫
Ne journalise jamais de secrets

Mots de passe, numéros de carte, jetons d’authentification : rien de tout ça ne doit apparaître dans un fichier de log. Un journal mal protégé devient lui-même une fuite de données.

A10 : la mauvaise gestion des conditions exceptionnelles

Deuxième nouveauté de 2025 : que fait ton application quand quelque chose se passe mal ? Une base de données indisponible, un fichier manquant, un service externe qui ne répond pas… La faille la plus dangereuse est l’application qui échoue en restant ouverte (fail open) : en cas d’erreur, elle laisse passer au lieu de bloquer.

// ❌ Fail open : si le service de vérification plante, tout le monde est autorisé
async function estAutorise(user) {
  try {
    return await serviceDroits.verifier(user);
  } catch {
    return true; // « pour ne pas bloquer les utilisateurs »… et les attaquants
  }
}

// ✅ Fail closed : en cas de doute, on refuse et on journalise
async function estAutorise(user) {
  try {
    return await serviceDroits.verifier(user);
  } catch (erreur) {
    console.error('Vérification des droits impossible', erreur);
    return false;
  }
}
JavaScript

Cette catégorie couvre aussi les messages d’erreur trop bavards (traces d’exception, requêtes SQL affichées à l’écran) et les erreurs logiques non prévues, comme une division par zéro dans un calcul de prix.

Comment tester ton application contre les failles web

Connaître l’OWASP Top 10, c’est bien ; vérifier que ton application y résiste, c’est mieux. Voici une démarche simple, accessible même sur un petit projet :

ÉtapeOutilCe que tu détectes
Analyse des dépendancesnpm audit, composer audit, DependabotA03 : paquets vulnérables
Analyse du code (SAST)Semgrep, SonarQube CommunityA05 : injections, A04 : crypto faible
Scan dynamique (DAST)OWASP ZAP (gratuit)A02 : en-têtes manquants, A05 : XSS
Vérification des en-têtesMozilla HTTP ObservatoryA02 : configuration HTTP
Revue manuelleToi, avec la checklist ci-dessousA01, A06 : logique et droits
⚠️
Teste uniquement ce qui t’appartient

Lancer un scanner de vulnérabilités sur un site qui n’est pas le tien est illégal dans la plupart des pays. Utilise ces outils sur tes propres applications, en local ou sur un environnement de test, ou avec l’accord écrit du propriétaire.

La checklist OWASP Top 10 à garder sous la main

  • [A01] Chaque requête vérifie les droits côté serveur, la ressource appartient bien à l’utilisateur.
  • [A02] Mode debug désactivé, en-têtes de sécurité présents, aucun fichier sensible exposé.
  • [A03] Dépendances auditées, fichier de verrouillage commité, alertes activées.
  • [A04] HTTPS partout, mots de passe hachés avec password_hash ou bcrypt.
  • [A05] Requêtes préparées partout, sorties HTML échappées.
  • [A06] Limites sur les actions sensibles, montants vérifiés côté serveur.
  • [A07] 2FA pour les administrateurs, limitation des tentatives, régénération de session.
  • [A08] SRI sur les scripts externes, pas d’unserialize() sur des données utilisateur.
  • [A09] Connexions et actions sensibles journalisées, alertes en cas d’anomalie.
  • [A10] En cas d’erreur, l’application refuse (fail closed) et affiche un message générique.
OWASP Top 10 : les points essentiels à retenir
Les points essentiels à retenir de ce guide.

FAQ

L’OWASP Top 10 est-il obligatoire ?

Non, ce n’est pas une norme légale. Mais il est cité comme référence dans de nombreux cahiers des charges, audits et certifications (comme PCI DSS pour le paiement en ligne). Le respecter est la base attendue de tout développeur professionnel.

Quelle est la différence entre l’OWASP Top 10 2021 et 2025 ?

L’édition 2025 ajoute deux catégories (la chaîne d’approvisionnement logicielle et la mauvaise gestion des conditions exceptionnelles), intègre le SSRF au contrôle d’accès et fait monter la mauvaise configuration de la 5e à la 2e place.

Un framework comme Laravel ou Symfony me protège-t-il de toutes les failles web ?

Il te protège par défaut contre beaucoup d’entre elles (injection SQL via l’ORM, XSS via le moteur de templates, CSRF). Mais il ne peut rien contre une logique mal conçue (A06) ou un contrôle d’accès oublié (A01) : ça reste ta responsabilité.

Par quelle faille web commencer quand on débute ?

Par l’injection (A05) et le contrôle d’accès (A01). Les requêtes préparées et la vérification des droits sur chaque ressource suppriment à elles seules une très grande partie des risques.

Conclusion

L’OWASP Top 10 n’est pas une liste à apprendre pour un examen : c’est une grille de lecture à appliquer à chaque fonctionnalité que tu développes. Contrôle d’accès, configuration, dépendances, chiffrement, injection… Si tu prends l’habitude de passer chaque nouvelle page au crible de ces dix catégories, tu élimines l’essentiel des failles web qui touchent les sites aujourd’hui.

Si tu développes en PHP, commence par un environnement propre et à jour grâce à notre guide pour installer PHP 8.4 et Composer sous Windows. Garde ensuite la checklist ci-dessus à portée de main, active l’audit des dépendances sur tous tes projets, et consulte les fiches pratiques OWASP Cheat Sheet Series quand tu as un doute. Pour aller plus loin, la page officielle du projet OWASP Top 10 détaille chaque catégorie avec la liste complète des CWE associées.

Comments

No comments yet. Why don’t you start the discussion?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *