La sécurité n’est pas une case que l’on coche et oublie. C'est un état d'esprit, un processus continu et une responsabilité que porte chaque développeur. Un seul bug de sécurité peut coûter des millions de dollars en dommages, détruire la confiance des utilisateurs bâtie au fil des années et même anéantir des entreprises. Mais la sécurité ne doit pas nécessairement être intimidante ou paralysante. Avec les bonnes connaissances et des pratiques établies, vous pouvez créer des applications robustes qui protègent vos utilisateurs.
Le paysage moderne des menaces
Le monde de la sécurité Web a radicalement changé ces dernières années. Les attaquants ne sont plus des pirates informatiques solitaires dans des sous-sols sombres : ce sont des organisations criminelles sophistiquées dotées de budgets de plusieurs millions, des États-nations aux ressources illimitées et des robots automatisés qui analysent Internet 24h/24 et 7j/7 à la recherche de vulnérabilités.
Le coût d’une attaque réussie a explosé. Nous ne parlons pas seulement de données volées. Il existe des amendes réglementaires massives en vertu du RGPD et de la LGPD, qui peuvent atteindre 4 % du chiffre d'affaires mondial annuel d'une entreprise. Il existe des coûts pour informer les utilisateurs concernés, pour les enquêtes médico-légales, pour la remédiation des systèmes et pour la surveillance du crédit des victimes. Et il y a le coût incommensurable d’une réputation détruite : des utilisateurs qui perdent confiance et ne reviennent jamais.
Mais le scénario a également évolué du côté de la défense. Nous disposons de meilleurs outils, de cadres plus sécurisés par défaut, de services gérés qui suppriment des classes entières de vulnérabilités. Les fournisseurs de cloud investissent des milliards dans la sécurité des infrastructures. La communauté open source trouve et corrige rapidement les vulnérabilités. Si vous tirez parti de ces outils et suivez les meilleures pratiques, vous avez de réelles chances de garder une longueur d’avance sur les attaquants.
Les vulnérabilités qui comptent vraiment
L'OWASP publie une liste régulièrement mise à jour des 10 vulnérabilités Web les plus critiques. Cette liste n’est pas académique : elle est basée sur des attaques réelles et réussies qui coûtent de l’argent et des données aux entreprises. Explorons les plus importants et comment vous défendre.
Panne du contrôle d'accès
Il s’agit de la vulnérabilité numéro un pour une raison simple : elle est incroyablement courante et dévastatrice. L'idée de base est que les utilisateurs peuvent accéder à des ressources qu'ils ne devraient pas. Bob peut voir les commandes d'Alice. Un utilisateur normal peut accéder aux points de terminaison administratifs. Un client peut modifier les prix des produits en ajoutant un paramètre à l'URL.
L’erreur fondamentale ici est de s’appuyer sur la contribution de l’utilisateur pour prendre des décisions en matière de sécurité. "Je vais masquer ce bouton d'administration dans l'interface utilisateur" n'est pas une sécurité : toute personne connaissant l'URL peut y accéder. "Je vais mettre des identifiants séquentiels dans l'URL" est une invitation à parcourir les ressources.
La défense commence par la vérification des autorisations sur chaque point de terminaison. Pas seulement dans l’interface utilisateur, mais aussi dans le backend, dans chaque opération sensible. Chaque demande doit répondre à trois questions : qui fait cette demande ? Sont-ils authentifiés ? Sont-ils spécifiquement autorisés à effectuer cette action sur cette ressource spécifique ?
Utilisez ** des identifiants non devinables
vel** en tant qu'UUID au lieu d'ID séquentiels. Implémentez des politiques de moindre privilège : les utilisateurs ne doivent disposer que des autorisations minimales requises pour leurs rôles. Et testez de manière agressive : essayez d'accéder aux ressources en tant qu'autre utilisateur, en tant qu'utilisateur non authentifié, avec des identifiants modifiés.
Failles cryptographiques
Les données sensibles fuient constamment parce qu’elles n’ont pas été suffisamment protégées. Cela inclut les mots de passe stockés en texte clair ou faiblement hachés, les données de carte de crédit non cryptées, les jetons de session prévisibles et les sauvegardes de bases de données non protégées.
Le principe fondamental est de chiffrer les données sensibles au repos et en transit. HTTPS (TLS) n'est facultatif pour aucun site Web sur l'Internet public - les navigateurs modernes marquent même les sites Web HTTP comme « non sécurisés ». Heureusement, les certificats TLS sont gratuits avec Let's Encrypt.
Pour les données au repos, utilisez le chiffrement fort. AES-256 pour les données symétriques. N'implémentez jamais votre propre cryptographie - utilisez des bibliothèques bien établies et auditées. Pour les mots de passe en particulier, utilisez des algorithmes de hachage conçus pour les mots de passe comme bcrypt, scrypt ou Argon2. Celles-ci sont intentionnellement lentes, ce qui rend les attaques par force brute peu pratiques, même avec du matériel moderne.
La gestion des clés est souvent le maillon faible. Les clés de chiffrement ne peuvent pas être codées en dur dans le code ou les fichiers de configuration du référentiel. Utilisez des services de gestion des secrets comme AWS Secrets Manager, Google Secret Manager ou HashiCorp Vault. Faites régulièrement pivoter les clés. Avoir des procédures pour révoquer les clés compromises.
###Injection
L'injection SQL est toujours répandue car elle est facile à introduire accidentellement et dévastatrice lorsqu'elle est exploitée. Mais la catégorie d'injection est plus large : elle comprend l'injection de commandes, l'injection LDAP, l'injection NoSQL et l'injection de modèles.
Le modèle courant consiste à faire confiance aux entrées de l’utilisateur sans une vérification appropriée, permettant aux attaquants d’injecter des commandes malveillantes. Un attaquant peut extraire l'intégralité de votre base de données, supprimer des tables, modifier des données ou même prendre le contrôle du serveur.
La défense principale consiste en instructions préparées et requêtes paramétrées. Au lieu de concaténer des chaînes pour créer des requêtes SQL, vous utilisez des espaces réservés remplis de valeurs. La base de données traite ces valeurs comme des données, et non comme des commandes, rendant l'injection impossible.
Les ORM modernes comme Prisma, TypeORM ou Sequelize le font par défaut, donc l'utilisation de ces frameworks vous protège déjà dans la plupart des cas. Mais vous devez quand même faire attention aux requêtes brutes lorsque cela est nécessaire.
Une validation rigoureuse des entrées est une autre ligne de défense. Si vous attendez un nombre, vérifiez qu’il s’agit bien d’un nombre. Si vous attendez une date, validez le format. Si vous attendez un choix dans une liste prédéfinie, vérifiez que la valeur figure dans cette liste. Ne présumez jamais que les données des clients sont sécurisées ou bien formées.
Scripts intersites (XSS)
XSS permet aux attaquants d'injecter du JavaScript malveillant qui s'exécute dans les navigateurs des victimes. Cela peut voler des cookies de session, modifier le contenu des pages, rediriger vers des sites de phishing ou installer des enregistreurs de frappe.
Il existe trois types principaux : XSS stocké (le script malveillant est enregistré dans la base de données et exécuté à chaque chargement de la page), XSS réfléchi (le script provient d'un paramètre d'URL et est reflété dans la réponse) et XSS basé sur DOM (la vulnérabilité est dans JavaScript côté client).
La défense commence par des sorties d'échappement. Lorsque vous placez des données utilisateur au format HTML, JavaScript, CSS ou URL, vous devez échapper les caractères spéciaux de manière appropriée à ce contexte. Les frameworks modernes comme React le font automatiquement dans la plupart des cas, mais vous pouvez toujours introduire XSS en utilisant dangerouslySetInnerHTML ou similaire.
La Content Security Policy (CSP) constitue une puissante ligne de défense supplémentaire. Il s'agit d'un en-tête HTTP qui spécifie quelles polices de script, styles, images, etc. sont autorisés. Même si un attaquant parvient à injecter du code, CSP peut empêcher son exécution. Commencez par une politique restrictive et ouvrez-la si nécessaire.
Les cookies HTTP uniquement pour les jetons de session empêchent JavaScript d'accéder à ces cookies, atténuant ainsi l'impact de XSS. Si un attaquant ne peut pas voler le cookie de session, l'attaque est moins efficace.
Exposition de données sensibles
Journaux, messages d'erreur, réponses API : ce sont tous des endroits où des données sensibles peuvent être accidentellement divulguées. Une trace détaillée de la pile en production peut révéler la structure du code et ses dépendances. Les messages d'erreur SQL peuvent exposer le schéma de base de données. Les journaux peuvent contenir des mots de passe ou des jetons si vous n'y faites pas attention.
Le principe est de supposer que tout ce que vous envoyez au client peut être vu par les attaquants. Cela signifie ne jamais compter sur la « sécurité par l’obscurité », c’est-à-dire cacher des informations dans l’espoir que personne ne les trouvera. Utilisez une véritable authentification et une véritable autorisation.
Différents messages d'erreur en production et en développement sont une bonne pratique. En développement, vous souhaitez des traces de pile détaillées pour le débogage. En production, les utilisateurs (et les attaquants) devraient voir des messages génériques tels que « Une erreur inattendue s'est produite ».
Il est essentiel de filtrer soigneusement les journaux. Configurez votre enregistreur pour ne pas enregistrer les champs sensibles tels que les mots de passe, les jetons, les numéros de carte de crédit. Utilisez le masquage - enregistrez uniquement les 4 derniers chiffres d'une carte, par exemple.
Authentification et autorisation robustes
Ce sont les gardiens de votre système. L'authentification vérifie l'identité (qui vous êtes), l'autorisation vérifie les autorisations (ce que vous pouvez faire). Les erreurs ici sont catastrophiques.
Mots de passe et identifiants
Exiger des mots de passe forts est un bon début, mais définir correctement « fort » est important. Les règles arbitraires telles que « doit avoir un type pour chaque caractère » sont moins efficaces que la simple exigence d'une longueur minimale de 12 à 16 caractères. Des phrases secrètes longues mais mémorables valent mieux que des mots de passe courts et complexes que les utilisateurs écrivent sur des post-its.
Ne stockez jamais les mots de passe en texte clair. Utilisez des algorithmes de hachage appropriés. Bcrypt avec un facteur de coût d'au moins 10 est un bon standard. Le hachage doit être suffisamment lent pour rendre la force brute peu pratique, mais pas au point de dégrader l'expérience utilisateur.
La limitation du débit sur les points de terminaison de connexion empêche les attaques par force brute. Après quelques tentatives infructueuses, exigez CAPTCHA ou bloquez temporairement. Utilisez une interruption exponentielle : chaque tentative échouée augmente le temps de recharge.
L'authentification multifacteur (MFA) ajoute une couche de sécurité critique. Même si le mot de passe est divulgué, les attaquants ne peuvent pas accéder au compte sans le deuxième facteur. Les TOTP (time codes) via des applications comme Google Authenticator ou Authy sont bons. Les SMS sont mieux que rien mais vulnérables à l’échange de carte SIM. WebAuthn avec des clés matérielles (YubiKey) est la référence.
Gestion des sessions
Les jetons de session sont essentiellement les clés de votre application. Si un attaquant vole un jeton valide, il peut usurper l'identité de l'utilisateur.
Les jetons de session doivent être véritablement aléatoires et imprévisibles. Utilisez des générateurs cryptographiquement sécurisés, pas Math.random(). Les jetons doivent avoir une entropie suffisante - au moins 128 bits sont recommandés.
L'expiration de la session équilibre commodité et sécurité. De très longues sessions constituent un risque en cas de fuite du jeton. Trop court frustre les utilisateurs. Pensez à utiliser des jetons d'actualisation : des jetons d'accès de courte durée (15 à 30 minutes) qui sont renouvelés avec des jetons d'actualisation de longue durée mais nécessitent une réauthentification périodique.
L'invalidation des anciennes sessions lorsque l'utilisateur se déconnecte ou change de mot de passe est cruciale. Les attaquants ne devraient pas pouvoir utiliser les jetons volés une fois que la victime a réalisé la compromission.
OAuth et OpenID Connect
Dans la plupart des cas, n'implémentez pas l'authentification vous-même. Utilisez des fournisseurs établis comme Auth0, AWS Cognito, Firebase Auth ou une connexion sociale (Google, GitHub, Microsoft). Ces services spécialisés disposent d’équipes entières dédiées à la sécurité de l’authentification.
Si vous devez absolument mettre en œuvre, utilisez les normes établies. OAuth 2.0 pour l'autorisation, OpenID Connect pour l'authentification. N'inventez pas votre propre système : le terrain regorge de pièges subtils qu'il est facile de rater.
PKCE (Proof Key for Code Exchange) doit être utilisé même dans les applications non publiques pour empêcher les attaques par interception de code d'autorisation. Il s'agit d'une petite surcharge qui élimine une classe de vulnérabilités.
Défense en profondeur
Ne comptez pas sur une seule couche de protection. Supposons que chaque couche puisse échouer et implémenter plusieurs couches indépendantes.
Le pare-feu d'application Web (WAF) filtre le trafic malveillant avant qu'il n'atteigne votre application. Des services comme Cloudflare, AWS WAF ou Azure WAF bloquent les signatures d'attaque connues
. Cela ne remplace pas le code sécurisé, mais c'est une couche supplémentaire précieuse.
La limitation du débit et la limitation empêchent les abus des API. Limitez le nombre de requêtes qu’un utilisateur peut effectuer par minute/heure. Cela atténue les DDoS, la force brute et le scraping agressif.
La surveillance et les alertes détectent les comportements suspects. De nombreux échecs de connexion à partir d'une seule adresse IP ? Activité anormale d'un compte normalement inactif ? Des tentatives d'accès aux points de terminaison administratifs par des utilisateurs réguliers ? Ces modèles devraient déclencher des alertes et des enquêtes.
Le Plan de réponse aux incidents garantit que vous savez quoi faire quand (et non si) une attaque se produit. Qui est informé ? Comment isoler le système ? Comment communiquer avec les utilisateurs ? Comment récupérer à partir de sauvegardes ? Avoir un playbook réduit considérablement le temps de réponse.
Conclusion
La sécurité Web est un domaine vaste et en constante évolution. De nouvelles vulnérabilités sont découvertes, de nouveaux modèles d'attaque émergent, de nouveaux outils de défense sont créés. Vous ne saurez jamais tout, mais vous pouvez établir des principes et des processus solides qui vous permettront de devancer la plupart des menaces.
Commencez par les fondamentaux : [authentification robuste, contrôle d'accès approprié, validation des entrées, sorties échappées, cryptage des données sensibles. Utilisez des frameworks et des bibliothèques bien établis plutôt que de réinventer la roue. Gardez les dépendances à jour. Testez régulièrement. Surveillez en permanence.
La sécurité n'est pas un projet que vous réalisez : c'est une pratique continue que vous intégrez dans tous les aspects du développement. Traitez-le avec le sérieux qu'il mérite, car vos utilisateurs vous confient leurs données.
Comment abordez-vous la sécurité dans vos projets ? Avez-vous fait face à des incidents de sécurité ? Partagez vos expériences !
A lire aussi
- Sécurité dans les applications Web -Sécurité dans les applications Web - Architecture pour les entreprises
- La sécurité dans les applications web : l'architecture expliquée aux débutants
- La sécurité dans les applications web : les fondamentaux que personne ne peut ignorer
- Sécurité API -API Security : les étapes essentielles pour ne pas laisser la porte ouverte
