OWASP Top 10 Avancé

Maîtriser l'OWASP Top 10 : Vulnerabilités Critiques et Stratégies de Défense Avancées

Un plongeon profond dans les 10 vulnérabilités les plus dangereuses des applications web avec focus sur les mécanismes internes, les cas limites et les techniques de détection expert. Comprenez comment les attaquants exploitent chaque faille et implémentez des défenses sophistiquées.

Preparetoi.academy 30 min

1. Injection SQL : Anatomie d'une Vulnérabilité Structurelle

L'injection SQL représente l'une des vulnérabilités applicatives les plus anciennes et les plus persistantes, classée #1 dans l'OWASP Top 10. Elle survient lorsqu'une application construit dynamiquement des requêtes SQL en concaténant directement des données utilisateur sans sanitisation appropriée. Le résultat : un attaquant peut modifier la logique même de la requête, contourner l'authentification, extraire des données sensibles ou altérer/supprimer des enregistrements.

Définition précise : L'injection SQL est l'insertion malveillante de code SQL dans des paramètres d'entrée, exploitant une absence de séparation entre les données et le code exécutable. Cette faille permet l'exécution de commandes SQL arbitraires sur le serveur de base de données.

Analogie pertinente : Imaginez un formulaire de commande dans un restaurant. L'employé lit votre demande : "Je veux une pizza". Mais si vous dites "Je veux une pizza; donnez-moi tous les numéros de carte de crédit clients", l'employé naïf exécutera exactement cet ordre en deux étapes. Avec les requêtes SQL, c'est identique : le moteur SQL exécute tout ce qu'il voit comme commande valide.

Aspect Détail
Vecteur d'attaque Champs de formulaire, paramètres URL, en-têtes HTTP
Impact critique Confidentialité (extraction), Intégrité (modification), Disponibilité (suppression)
Exemple basique SELECT * FROM users WHERE id = ' + userInput + '
Exemple avancé Union-based, Time-based blind, Error-based injection
Détection WAF, SIEM, analyse des logs anormaux, monitoring requêtes
Prévention primaire Prepared Statements, Parameterized Queries

Astuce d'expert : Utilisez les Prepared Statements (requêtes paramétrées) dans tous les langages modernes. Ces mécanismes envoient la structure SQL et les données séparément au serveur, garantissant que les données ne peuvent jamais altérer la logique. Pour MySQL : stmt = connection.prepareStatement("SELECT * FROM users WHERE id = ?") puis stmt.setInt(1, userId). Le serveur traite le "?" comme donnée pure, jamais comme code.

⚠️ Attention critique : Même avec des frameworks ORM (Hibernate, Entity Framework), les développeurs créent accidentellement des injections SQL en utilisant des concaténations dans les paramètres de requête. Exemple dangereux : session.createQuery("FROM User WHERE name = '" + username + "'"). Le framework n'est pas une balle magique.

Cas limites avancés : les injections dans les clauses ORDER BY et LIMIT ne sont pas toujours couvertes par les prepared statements standards. Les requêtes dynamiques complexes, les stored procedures construites dynamiquement, et les injections au niveau du commentaire SQL exigent une défense en profondeur avec validation stricte des données.


2. Authentification Brisée : Quand l'Identité Devient un Mensonge

L'authentification brisée (#2 OWASP) ne se limite pas aux mots de passe faibles. C'est un écosystème entier de défauts : gestion de sessions défectueuse, prédictibilité des tokens, absence de MFA, implémentations cryptographiques incorrectes, et récupération d'identifiants mal sécurisée. Les attaquants exploitent ces fissures pour usurper l'identité d'utilisateurs légitimes.

Définition complète : L'authentification brisée englobe toute défaillance dans le mécanisme de vérification d'identité, incluant : tokens prédictibles, sessions non invalidées, absence de timeout, récupération de compte triviale, cookies non sécurisés, et endpoints d'authentification accessibles sans rate-limiting.

Analogie éducative : Imaginez un système d'accès à un immeuble. Un système faible : une porte avec une serrure qui reste ouverte pendant 24h (session infinie), une clé imprimée sur chaque porte (token prédictible), aucun gardien (pas de MFA), et un formulaire "oublié votre clé?" qui accepte n'importe quel nom (récupération de compte faible). Un attaquant n'a que l'embarras du choix.

Élément Attaque Possible Mitigation Avancée
Sessions Fixation, vol de cookie, réutilisation Regenerate ID après login, flags HttpOnly/Secure/SameSite
Tokens JWT Modification (sans validation), expiration infinie Signature RS256, short-lived + refresh tokens, kid validation
Mots de passe Force brute, rainbow tables, spray attack Bcrypt/Argon2 + salt aléatoire, rate-limiting/CAPTCHA, MFA obligatoire
MFA Bypass, SMS interception, TOTP faible WebAuthn (FIDO2), backup codes chiffrés, push notifications
Récupération Réinitialisation réversible, questions triviales Tokens jetables/expirables, confirmation email temporelle, MFA pour réinitialiser

Astuce d'expert : Pour JWT, implémentez une rotation de clés asymétriques avec versioning via l'en-tête "kid" (Key ID). Votre serveur maintient plusieurs clés publiques, et le JWT indique quelle clé utiliser : {"kid": "2024-01-15", "alg": "RS256"}. Si une clé est compromise, vous basculez immédiatement sans invalider tous les tokens existants. Côté serveur, maintenez une liste noire de tokens révoqués (Redis avec TTL égal au reste de validité du token).

⚠️ Attention critique : La plupart des brèches d'authentification proviennent de l'absence de MFA, pas de la faiblesse du mot de passe lui-même. Même avec des mots de passe de 128 caractères, sans MFA, un attaquant avec vos identifiants d'accès à votre gestionnaire de mots de passe ou via phishing obtient tout. Imposez MFA basée sur des appareils (WebAuthn) plutôt que SMS (interceptable). Les entreprises doivent bloquer les authentifications sans MFA après une période de transition.

Cas limites avancés : les attaques de timing sur les vérifications de token (comparer deux tokens byte-par-byte prend plus de temps s'ils correspondent au début), les sessions distribuées sur microservices sans synchronisation centrale, les implémentations "custom" de MFA qui réinventent la roue de manière vulnérable, et les tokens d'authentification stockés en localStorage (vulnérables à XSS) plutôt qu'en httpOnly cookies.


3. Injection de Commandes et Template : Exécution de Code Réelle

L'injection de commandes (#3 OWASP) et ses variantes (template injection, expression language) permettent aux attaquants d'exécuter du code arbitraire sur le serveur. Contrairement à l'injection SQL qui modifie une requête, ces failles exécutent du code système ou du code applicatif interprété.

Définition rigoureuse : L'injection de commandes est le passage de paramètres utilisateur non validés à des fonctions d'exécution système (exec, system, popen) ou d'évaluation (eval, template rendering non sandboxé). Cela permet l'exécution de commandes shell ou code applicatif arbitraire avec les privilèges du processus serveur.

Analogie parlante : Un développeur crée une fonction "ping utilitaire" : exec("ping " + user_input). Un utilisateur rentre normalement "google.com", et la commande devient ping google.com. Mais un attaquant rentre "google.com; rm -rf /" et obtient ping google.com; rm -rf / — deux commandes exécutées. Le serveur supprime l'intégralité du répertoire racine.

Type d'Injection Mécanisme Exemple d'Exploit Détection
Commandes OS exec(), system() ; whoami; cat /etc/passwd Caractères spéciaux ;, `
Template SSTI Template rendering non échappé {{7*7}} → 49 (évaluation), ${Runtime.exec()} Délimiteurs template + payload polyglotte
Expression Language (EL) ${...} non validé ${request.getSession().setAttribute(...)} Parenthèses, appels de méthode dans paramètres
XPath Injection Requêtes XPath dynamiques admin' or '1'='1 dans XPath Structure arborescente exploitée
Prévention universelle Listes blanches strictes Regex /^[a-zA-Z0-9._-]+$/ pour noms fichiers Validation avant tout traitement

Astuce d'expert : Pour les commandes OS, utilisez TOUJOURS des APIs paramétrées plutôt que shell. En Python : subprocess.run(["ping", user_input], check=True) (liste de paramètres séparés) au lieu de os.system("ping " + user_input). Le tableau de paramètres empêche l'interprétation des métacaractères shell. Pour les templates (Jinja2, Thymeleaf), ne passez JAMAIS les données utilisateur directement au moteur de template. Isolez le contexte : les développeurs peuvent utiliser des expressions template, pas les utilisateurs.

⚠️ Attention critique : Les développeurs utilisant des expressions EL dans les templates croyent à tort que l'escaping HTML suffit. Escaper < en &lt; protège contre XSS, mais pas contre ${7*7} qui s'évalue à 49 avant l'escaping. Dans Thymeleaf, utilisez toujours th:text="${var}" (texte brut) au lieu de th:utext="${var}" (HTML non échappé), et JAMAIS le mode /*[# th:inline="javascript" #]*/ qui crée des brèches EL.

Cas limites : les injections de commande dans les expressions régulières (regex DOS via répétitions), les injections dans les paramètres de bases de données NoSQL qui acceptent des expressions JavaScript, les injections à travers les fichiers de configuration malveillants (YAML, XML), et les escapes unicode qui contournent les filtres simples (\u003b = ;).


4. Exposition de Données Sensibles : Quand le Secret N'en Est Plus Un

L'exposition de données sensibles (#4 OWASP) couvre l'absence de chiffrement, la transmission en clair, le stockage non sécurisé et l'accès non contrôlé. Les données sensibles incluent mots de passe, numéros de carte bancaire, données de santé, tokens d'authentification — tout ce qui pourrait nuire s'il était exposé.

Définition exhaustive : L'exposition de données sensibles est l'absence de contrôle d'accès, de chiffrement, ou de masquage approprié de données confidentielles. Elle inclut : transmission HTTP au lieu de HTTPS, stockage en clair, hachage faible (MD5/SHA1), logs contenant des secrets, backups non chiffrés, et accès sans restriction à des APIs renvoyant trop de données.

Analogie explicite : Imaginez un hôpital qui envoie vos résultats médicaux par courrier postal ouvert (HTTP). Un facteur voyeur litéralement vos données, tout le monde sur le chemin les voit. L'hôpital stocke aussi les dossiers dans un classeur sans cadenas dans le couloir (base de données publique sans authentification). Un agent de sécurité peut photographier 500 dossiers en 10 minutes (API sans rate-limiting retournant des données en masse).

Protection Technique Implémentation Cas Limites
En transit TLS 1.3 https://, certificats valides, HSTS header Downgrade attacks, versions anciennes supportées
Au repos AES-256-GCM Chiffrement de colonne database, disques chiffrés Clés stockées avec données, pas de rotation
Hachage Argon2id, Bcrypt bcrypt.hashpw(password, bcrypt.gensalt(12)) Rainbow tables si salt faible, GPU attacks
API Pagination + filtrage SELECT * LIMIT 10 OFFSET x WHERE user_id = current_user() Field-level exposure, N+1 queries révélant structure
Logs/Monitoring Tokenization, redaction Remplacer ssn par token UUID, hacher IPs sensibles Logs dans des fichiers world-readable, backups cloud

Astuce d'expert : Implémentez le chiffrement de colonne avec une clé maître distante. Exemple : votre application chiffre les numéros de carte bancaire avec une clé stockée dans un coffre (AWS KMS, HashiCorp Vault). Même si quelqu'un accède directement à la base de données, les données restent illisibles. Pour les logs, utilisez une fonction de tokenization : tout numéro de carte devient automatiquement un token jetable pendant le logging, avec une table de mapping chiffrée ailleurs. Implémentez aussi l'oubli sur demande : une API interne qui supprime physiquement les données des répliques, backups et caches après une requête RGPD.

⚠️ Attention critique : Beaucoup d'équipes utilisent SSL/TLS (HTTPS) mais stockent les secrets directement dans la base de données en clair. C'est comme enfermer votre porte à clé mais laisser le code de la clé écrit sur le mur. De plus, l'absence d'audit et de monitoring des accès sensibles est un risque. Qui a accédé aux données des clients? À quelle heure? Quels champs? Sans logs d'accès indexés et alertes, vous découvrirez la fuite 6 mois après.

Cas limites avancés : les données sensibles dans les URLs de redirection (tokens en paramètre GET au lieu de POST body), les informations révélées par timing d'erreurs ou messages d'erreur différents ("mot de passe incorrect" vs "utilisateur inexistant" informe un attaquant de l'existence d'un compte), les métadonnées de fichiers téléchargés (EXIF de photos révélant localisation GPS), les données dans les requêtes GraphQL non validées (un utilisateur requêtant des champs non autorisés), et les caches non validés qui stockent données sensibles (Redis sans authentification ou chiffrement).


5. Contrôle d'Accès Brisé : Contourner les Frontières d'Autorisation

Le contrôle d'accès brisé (#5 OWASP) est techniquement simple mais répandu : un utilisateur accède à des ressources qu'il n'est pas autorisé à consulter, modifier ou supprimer. Contrairement à l'authentification (qui a-t-on?), l'autorisation (que peut-on faire?) est souvent une réflexion tardive dans le développement.

Définition précise : Le contrôle d'accès brisé est l'absence ou l'implémentation inadéquate de vérifications d'autorisation. Un utilisateur authentifié accède à des données, fonctionnalités ou ressources d'un autre utilisateur, ou un utilisateur normal exécute des actions administrateur. Les types incluent : escalade de privilèges horizontale (accès aux données d'un pair), verticale (accès aux droits d'admin), et accès à des ressources par énumération.

Analogie transparente : Un immense bâtiment de bureau. La sécurité vérifie votre identité à l'entrée (authentification) et vous remet un badge. Mais le contrôle d'accès brisé : votre badge d'employé niveau 1 ouvre aussi les salles de réunion exécutive (escalade verticale), les dossiers d'autres employés (escalade horizontale), et en changeant le numéro de salle de 101 à 102 dans l'URL de la porte électronique, vous entrez partout (IDOR — Insecure Direct Object Reference).

Type d'Accès Brisé Scénario Réel Détection Mitigation
IDOR /api/profile/123 → modifier 124 Énumération séquentielle, pas de vérification propriété Vérifier current_user == resource.owner avant tout
Escalade verticale Admin panel accessible sans rôle admin Accès direct à URLs /admin/* Middleware d'autorisation avec rôles
Escalade horizontale Voir les commandes d'un autre client API retourne toutes commandes sans filtre user WHERE user_id = current_user_id obligatoire
Accumulation de privilèges Obtenir des permissions jamais révoquées Changements de rôle non auditables Audit trail, révocation proactive
Chemins non protégés Accès direct à téléchargements privés URLs prédictibles (/uploads/doc_12345.pdf) Tokens temporaires, redirection via vérification
API non documentées Endpoints internes sans sécurité Reconnaissance via JavaScript, graphql/introspection Rate-limiting, authentification stricte

Astuce d'expert : Implémentez un middleware d'autorisation déclaratif. Dans un framework comme Express/Node.js, créez une fonction authorize :

async function authorize(requiredRole, resourceOwnerField) {
  return (req, res, next) => {
    const user = req.user;
    const resource = req.resource;
    
    if (!user.roles.includes(requiredRole)) {
      return res.status(403).json({error: "Forbidden"});
    }
    
    if (resourceOwnerField && resource[resourceOwnerField] !== user.id) {
      return res.status(403).json({error: "Not your resource"});
    }
    
    next();
  };
}

// Utilisation :
app.get('/api/orders/:id', 
  loadResource('Order'),
  authorize('customer', 'user_id'), // Vérifier rôle ET propriété
  (req, res) => { /* ... */ }
);

Cela garantit que CHAQUE endpoint vérifie les droits avant retourner des données. Pour les données sensibles (numéros de compte), masquez les partiellement : ****1234 au lieu du numéro complet.

⚠️ Attention critique : Le plus grand piège : les développeurs supposent que si une page n'est pas liée dans l'interface, elle est sécurisée. FAUX. Un attaquant qui connaît l'existence de /api/admin/users y accède directement. Toutes les vérifications d'autorisation doivent être au niveau du code métier (routes, contrôleurs), pas juste via l'UI. De plus, les contrôles côté client (vérifier if (user.isAdmin) en JavaScript) sont complètement inefficaces — modifiez une variable locale et hop, vous êtes admin.

Cas limites avancés : les permissions temporaires ou contextuelles (un utilisateur est admin d'une organisation mais pas d'une autre — oublier le contexte dans une requête croisée), les dépendances de permission non transitive (accès à une ressource enfant n'implique pas accès au parent, mais accès parent oui enfants), les permissions basées sur les attributs (ABAC) où la logique est complexe et non testée (données sensibles selon région + département + heure du jour), et les débordements de permissions après rotation de rôles (un ancien admin conserve des clés API non révoquées).

Accédez à des centaines d'examens QCM — Découvrir les offres Premium