Les 10 risques les plus critiques en matière de sécurité des applications web et la manière de les atténuer.
Le Top 10 de l'OWASP est le document de sensibilisation à la sécurité des applications web le plus largement reconnu. L'édition 2025 reflète le paysage des menaces le plus récent, en introduisant de nouvelles catégories telles que les défaillances de la chaîne d'approvisionnement des logiciels et la mauvaise gestion des conditions exceptionnelles.
Le contrôle d'accès permet d'appliquer la politique de manière à ce que les utilisateurs ne puissent pas agir en dehors des autorisations qui leur ont été accordées. Les défaillances conduisent généralement à la divulgation d'informations non autorisées, à la modification ou à la destruction de données, ou à l'exécution d'une fonction commerciale en dehors des limites de l'utilisateur.
Les attaquants peuvent exploiter les failles du contrôle d'accès pour accéder aux comptes d'autres utilisateurs, consulter des fichiers sensibles, modifier les données d'autres utilisateurs ou changer les droits d'accès.
# User ID taken directly from request without authorization check @app.route('/api/user/<user_id>/profile') def get_profile(user_id): user = db.get_user(user_id) # No ownership check! return jsonify(user.to_dict())
@app.route('/api/user/<user_id>/profile') @login_required def get_profile(user_id): # Verify the requesting user owns this resource if current_user.id != user_id and not current_user.is_admin: abort(403) user = db.get_user(user_id) if not user: abort(404) return jsonify(user.to_dict())
Il y a mauvaise configuration de la sécurité lorsque les paramètres de sécurité sont définis, mis en œuvre ou maintenus de manière incorrecte. Il s'agit notamment de l'absence de renforcement de la sécurité, de l'activation de fonctions inutiles, de comptes par défaut avec des mots de passe inchangés, de messages d'erreur trop verbeux et d'en-têtes de sécurité HTTP mal configurés.
Des serveurs, des frameworks ou des services en nuage mal configurés peuvent exposer des données sensibles, permettre un accès non autorisé ou fournir aux attaquants des informations leur permettant de planifier d'autres attaques.
# Debug mode enabled in production, verbose errors exposed app = Flask(__name__) app.config['DEBUG'] = True # Exposes stack traces! app.config['SECRET_KEY'] = 'default-secret' # Default key!
import os app = Flask(__name__) app.config['DEBUG'] = False app.config['SECRET_KEY'] = os.environ['SECRET_KEY'] @app.after_request def set_security_headers(response): response.headers['X-Content-Type-Options'] = 'nosniff' response.headers['X-Frame-Options'] = 'DENY' response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' response.headers['Content-Security-Policy'] = "default-src 'self'" return response
Nouveauté 2025. Se concentre sur les risques liés aux composants tiers, aux dépendances et aux pipelines CI/CD. Les attaquants ciblent la chaîne d'approvisionnement des logiciels en compromettant les paquets, en injectant du code malveillant dans les bibliothèques open-source ou en exploitant les vulnérabilités des pipelines de construction.
Une seule dépendance compromise peut affecter des milliers d'applications. Les attaques de la chaîne d'approvisionnement peuvent conduire au vol de données, à l'installation d'une porte dérobée ou à la compromission complète du système avec une détection minimale.
// package.json with unpinned dependencies { "dependencies": { "express": "*", // Any version! "lodash": "^4.0.0", // Wide range "unknown-pkg": "^1.0.0" // Unvetted package } }
// package.json with pinned versions + lockfile + audit { "dependencies": { "express": "4.21.2", "lodash": "4.17.21" }, "scripts": { "preinstall": "npx npm-audit-resolver", "integrity-check": "npm audit signatures" } } // Also: use package-lock.json, enable Dependabot/Renovate, // generate SBOM, verify package provenance
Défaillances liées à la cryptographie qui conduisent souvent à l'exposition de données sensibles. Il s'agit notamment de l'utilisation d'algorithmes obsolètes, de la génération de clés faibles, de l'absence de chiffrement des données en transit ou au repos, et de la validation incorrecte des certificats.
Un chiffrement faible ou manquant peut exposer des mots de passe, des numéros de carte de crédit, des dossiers médicaux, des informations personnelles et des secrets d'affaires, ce qui peut entraîner des violations de la réglementation (GDPR, PCI DSS).
import hashlib # Storing passwords with weak hashing def store_password(password: str) -> str: return hashlib.md5(password.encode()).hexdigest() # MD5 is broken!
import bcrypt def store_password(password: str) -> bytes: # Use bcrypt with automatic salting salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode(), salt) def verify_password(password: str, hashed: bytes) -> bool: return bcrypt.checkpw(password.encode(), hashed)
Les failles d'injection se produisent lorsque des données non fiables sont envoyées à un interpréteur dans le cadre d'une commande ou d'une requête. L'injection SQL, l'injection NoSQL, l'injection de commandes OS et le Cross-Site Scripting (XSS) sont les formes les plus courantes. Des données hostiles peuvent inciter l'interprète à exécuter des commandes non souhaitées.
L'injection peut entraîner la perte ou la corruption de données, un accès non autorisé, une prise de contrôle complète de l'hôte ou un déni de service. L'injection SQL reste à elle seule l'un des vecteurs d'attaque les plus dangereux et les plus répandus.
# SQL injection via string concatenation def get_user(username: str): query = f"SELECT * FROM users WHERE name = '{username}'" return db.execute(query) # username = "' OR '1'='1"
# Parameterized queries prevent SQL injection def get_user(username: str): query = "SELECT * FROM users WHERE name = %s" return db.execute(query, (username,)) # For XSS prevention, escape output from markupsafe import escape def render_comment(comment: str) -> str: return f"<p>{escape(comment)}</p>"
La conception non sécurisée fait référence aux risques liés aux défauts de conception et d'architecture. Elle nécessite l'utilisation de la modélisation des menaces, de modèles de conception sécurisés et d'architectures de référence. Une conception non sécurisée ne peut être corrigée par une mise en œuvre parfaite ; les contrôles de sécurité nécessaires n'ont jamais été créés pour se défendre contre des attaques spécifiques.
Les défauts de conception peuvent entraîner des vulnérabilités au niveau de la logique d'entreprise qui sont difficiles à détecter à l'aide d'outils automatisés. L'absence de limites de taux pour les opérations sensibles, l'absence d'authentification multifactorielle pour les actions critiques ou la détection insuffisante des fraudes sont autant de défaillances au niveau de la conception.
# Password reset with no rate limit or verification @app.route('/reset-password', methods=['POST']) def reset_password(): email = request.form['email'] new_pass = request.form['new_password'] user = db.find_by_email(email) user.password = hash_password(new_pass) # No token verification! db.save(user)
from datetime import datetime, timedelta @app.route('/reset-password', methods=['POST']) @rate_limit("3/hour") def reset_password(): token = request.form['token'] new_pass = request.form['new_password'] # Verify time-limited, single-use token reset_req = db.find_reset_token(token) if not reset_req or reset_req.used or reset_req.expires < datetime.utcnow(): abort(400, "Invalid or expired token") # Enforce password complexity if not meets_password_policy(new_pass): abort(400, "Password does not meet requirements") reset_req.user.password = hash_password(new_pass) reset_req.used = True db.save_all([reset_req.user, reset_req])
La confirmation de l'identité d'un utilisateur, l'authentification et la gestion des sessions sont essentielles. Les échecs d'authentification se produisent lorsque les applications autorisent le bourrage d'identifiants, la force brute, les mots de passe faibles ou ont une gestion de session défectueuse telle que la non-rotation des identifiants de session après l'ouverture de la session.
Les attaquants peuvent accéder aux comptes d'utilisateurs par le biais d'un système automatisé de remplissage de justificatifs d'identité, de la force brute ou du détournement de session. Les comptes compromis peuvent conduire à l'usurpation d'identité, à la fraude et à la violation de données.
# No brute force protection, weak session handling @app.route('/login', methods=['POST']) def login(): user = db.find_by_email(request.form['email']) if user and user.password == request.form['password']: # Plain comparison! session['user'] = user.id # Session ID not rotated return redirect('/dashboard')
from flask_limiter import Limiter limiter = Limiter(app, default_limits=["100/hour"]) @app.route('/login', methods=['POST']) @limiter.limit("5/minute") def login(): user = db.find_by_email(request.form['email']) if not user or not bcrypt.checkpw( request.form['password'].encode(), user.password_hash ): # Generic error to prevent user enumeration return "Invalid credentials", 401 # Rotate session ID after authentication session.regenerate() session['user'] = user.id # Check for MFA requirement if user.mfa_enabled: return redirect('/mfa-verify') return redirect('/dashboard')
Les défaillances de l'intégrité des logiciels et des données sont liées au code et à l'infrastructure qui ne protègent pas contre les violations de l'intégrité. Il s'agit notamment de la désérialisation non sécurisée, de l'utilisation de CDN ou de plugins non fiables sans vérification de l'intégrité, et de mécanismes de mise à jour automatique sans mises à jour signées.
Une désérialisation non sécurisée peut conduire à l'exécution de code à distance. Le chargement de scripts à partir de CDN non fiables sans intégrité des sous-ressources (SRI) peut permettre à des attaquants d'injecter du code malveillant dans votre application.
<!-- Loading external scripts without integrity checks --> <script src="https://cdn.example.com/lib.js"></script> <!-- Insecure deserialization in Python --> import pickle data = pickle.loads(user_input) # Arbitrary code execution!
<!-- Use Subresource Integrity (SRI) for external scripts --> <script src="https://cdn.example.com/lib.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K..." crossorigin="anonymous"></script> # Use safe deserialization in Python import json data = json.loads(user_input) # Safe: only parses JSON data
En l'absence d'enregistrement, de surveillance et d'alerte suffisants, les violations ne peuvent pas être détectées à temps. Une journalisation insuffisante, une intégration inefficace avec les systèmes de réponse aux incidents et l'absence d'alerte en temps réel permettent aux pirates d'attaquer davantage les systèmes, de maintenir la persistance et d'altérer ou d'extraire des données.
En l'absence d'une journalisation appropriée, les attaquants peuvent agir sans être détectés pendant de longues périodes. La plupart des études sur les violations montrent que le temps moyen de détection d'une violation dépasse 200 jours, et que cette violation est souvent découverte par des parties externes plutôt que par un contrôle interne.
# No logging of security-relevant events @app.route('/login', methods=['POST']) def login(): user = authenticate(request.form) if not user: return "Login failed", 401 # No record of failure return redirect('/dashboard')
import logging from datetime import datetime security_log = logging.getLogger('security') @app.route('/login', methods=['POST']) def login(): user = authenticate(request.form) if not user: security_log.warning( "LOGIN_FAILED | ip=%s | email=%s | time=%s", request.remote_addr, request.form.get('email', 'unknown'), datetime.utcnow().isoformat(), ) check_brute_force(request.remote_addr) return "Invalid credentials", 401 security_log.info( "LOGIN_SUCCESS | user_id=%s | ip=%s", user.id, request.remote_addr, ) return redirect('/dashboard')
Nouveauté 2025. Les applications qui ne gèrent pas correctement les erreurs, les exceptions et les cas limites peuvent exposer des informations sensibles, entrer dans des états incohérents ou créer des conditions exploitables. Il s'agit notamment de messages d'erreur verbeux en production, d'exceptions non capturées qui contournent les contrôles de sécurité et de conditions de course.
Une mauvaise gestion des erreurs peut révéler des traces de pile, des détails de base de données ou des chemins internes. Les conditions de course dans les contrôles de sécurité peuvent permettre des attaques de type "temps de contrôle - temps d'utilisation" (TOCTOU), contournant l'autorisation ou la validation du paiement.
# Exposing internal details in error messages @app.route('/api/data') def get_data(): try: result = db.query(request.args['q']) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 # Leaks DB details!
import uuid, logging logger = logging.getLogger(__name__) @app.errorhandler(Exception) def handle_exception(e): error_id = str(uuid.uuid4()) logger.error("Unhandled exception [%s]: %s", error_id, e, exc_info=True) # Return generic error with reference ID for support return jsonify({ "error": "An internal error occurred", "reference": error_id, }), 500 @app.route('/api/data') def get_data(): q = request.args.get('q') if not q or not is_valid_query(q): return jsonify({"error": "Invalid query parameter"}), 400 result = db.query(q) return jsonify(result)
| ID | Vulnérabilité | Sévérité | Principales mesures d'atténuation |
|---|---|---|---|
| A01 | Contrôle d'accès défaillant | Critical | Contrôles d'accès côté serveur, refus par défaut, propriété des enregistrements |
| A02 | Mauvaise configuration de la sécurité | High | Processus de durcissement, en-têtes de sécurité, désactivation des valeurs par défaut |
| A03 | Défaillances de la chaîne d'approvisionnement en logiciels | High | Dépendances épinglées, SBOM, analyse des dépendances |
| A04 | Défaillances cryptographiques | High | Algorithmes solides, TLS 1.2+, gestion des clés |
| A05 | Injection | Critical | Requêtes paramétrées, codage de sortie, validation d'entrée |
| A06 | Insecure Design | High | Modélisation des menaces, modèles sécurisés, tests de cas d'abus |
| A07 | Défauts d'authentification | High | MFA, protection contre la force brute, rotation de session |
| A08 | Défauts d'intégrité des logiciels ou des données | High | SRI, mises à jour signées, désérialisation sûre |
| A09 | Défauts de journalisation et d'alerte en matière de sécurité | Medium | Enregistrement centralisé, alertes en temps réel, réponse aux incidents |
| A10 | Mauvaise gestion des conditions exceptionnelles | Medium | Gestionnaire d'erreurs global, messages génériques, prévention TOCTOU |