Checklist de sécurisation des développements Web

La checklist suivante peut être utilisée pour s’assurer que vos développements logiciels respectent les bonnes pratiques du moment en matière de sécurité applicative.
Ces vérifications se veulent indépendantes de la technologie utilisée pour le développement, ainsi que du système hôte et les recommandations sont applicables à la majorité des projets de développement Web.
Cette liste est découpée en 3 niveaux répondant chacun à un objectif initial de sécurité :

  • Niveau 1 : Application qui ne manipule pas de données personnelles ou sensibles
  • Niveau 2 : Application manipulant des données personnelles
  • Niveau 3 : Application manipulant des données sensibles (données de paiements, données médicales, etc.)
    • Niveau 1
    • Niveau 2
    • Niveau 3

    Conception

    • Définir l’architecture logicielle la plus simple et la plus petite possible en fonction de vos besoins métiers et des contraintes techniques que vous rencontrez

      Principe généraux d'architecture

      Les architectures complexes augmentent la probabilité que des erreurs soient introduites lors de la conception, du développement et de la maintenance. L’architecture doit :

      • Répondre aux besoins et exigences de la manière la plus simple possible
      • Faire en sorte que toutes les fonctionnalités aient leur place dans cette architecture (c’est-à-dire qu’aucune fonctionnalité ne doit être là où on ne l’attend pas, afin d’éviter les problèmes de perte et de réécriture de code)
      • Être conçue de façon à favoriser les améliorations: Le logiciel va forcément évoluer, aussi il faut qu’il soit le plus modulaire possible

      Des patterns de conception peuvent être utilisés pour cette tâche : MVC, N-tiers, « Gang of Four », etc.

    • Mettre en place des mécanismes de défense « en profondeur »

      Principes généraux d’architecture

      Faire en sorte que l’application ait plusieurs niveaux de défense successifs (de la couche supérieure accessible aux utilisateurs, aux couches plus basses).

      Cela permet de renforcer l’application et de bloquer un attaquant qui aurait passé un niveau de défense.

      Par exemple, un attaquant ayant accédé à certaines données en exploitant une faille sur l’interface utilisateur pourrait se retrouver bloqué par les permissions sur ces données.

    • Appliquer une politique du moindre privilège

      Principes généraux d’architecture

      N’accorder aux utilisateurs et éléments applicatifs que l’accès aux fonctionnalités, données et informations système nécessaires à l’accomplissement de leurs tâches.

      De manière générale, lorsque des privilèges sont accordés, ceux-ci doivent correspondre précisément au niveau requis pour l’exécution des tâches prévues. Si une élévation de privilèges est prévue pour une action en particulier, celle-ci ne doit être valable que le temps d’effectuer cette action.

      Pour la définition des permissions, on recommande d’utiliser un système d’habilitation par liste blanche : on autorise un ensemble d’actions réalisables par un utilisateur ou par un élément applicatif, et on refuse tout le reste.

    • Baser toutes les décisions d’accès sur la permission plutôt que sur l’exclusion

      Cloisonnement applicatif

      Il s’agit d’un système de validation par liste blanche : l’accès est refusé par défaut, à moins que les conditions nécessaires à une autorisation d’accès soient remplies.

      Cela s’applique à tous les utilisateurs et tous les composants soumis à une décision d’accès.

      Les validations par liste noire (autoriser tout ce qui n’est pas refusé) sont dangereuses, car lors de la définition des cas interdits, il est possible d’en oublier certains et d’autres peuvent ne pas être anticipés.

    • Exiger une authentification lorsqu’une connexion est réalisée vers un système extérieur qui contient des informations ou des fonctions sensibles

      Bonnes pratiques générales de développement

      Le système extérieur peut être un service tiers, une base de données, un serveur, etc. L’objectif principal est d’éviter les accès directs à ces composants par des attaquants.

      Les communications doivent se faire de façon sécurisée (eg. utilisation de TLS), même dans un LAN privé (eg. réseau interne).

      Les mécanismes d’authentification incluent d’une part les systèmes et protocoles utilisés pour l’authentification, mais également les secrets. L’authentification vers et depuis ces composants doit garantir l’identité du composant, mais également l’intégrité des requêtes effectuées entre les deux parties.

      Par exemple, pour l’accès à une base de données, il convient d’utiliser des informations d’authentification robustes et stockées de manière sécurisée.

    Code

    • Adopter et suivre un standard de qualité du développement

      Bonnes pratiques générales de développement

      La qualité d’un code produit va influer sur la qualité générale d’une application. Un code manquant de qualité aura potentiellement plus de chance de présenter des erreurs (bugs et/ou problèmes de sécurité), et sera plus difficile à relire, rendant la détection et la correction de ces erreurs plus compliquées. À long terme, cela impacte principalement la stabilité et la facilité de maintenance du code, et donc sa sécurité.

      Des bonnes pratiques de développement liées à la qualité du code, et également à la sécurité doivent être définies avant le début du projet, suivies par les développeurs et contrôlées tout au long du cycle de développement. Ces bonnes pratiques incluent les conventions de développement internes (convention de nommage, politiques de gestion d’erreurs, etc.) ainsi que les bonnes pratiques de qualité et de sécurité génériques et spécifiques aux technologies.

    • Séparer le code par fonctionnalités génériques autant que possible afin de faciliter leur réutilisation

      Bonnes pratiques générales de développement

      Lorsque l’on développe des fonctions qui ne sont pas spécifiques à une tâche particulière de l’application, et qui ont vocation à apparaître dans d’autres parties du code de l’application, il est fortement recommandé de développer ces fonctions de manière générique afin qu’elles soient réutilisables entre les parties du code.

      Cette pratique a plusieurs bienfaits :

      • Éviter la duplication de code et le redéveloppement de fonctionnalité identiques, afin de faire gagner du temps aux développeurs et de ne pas augmenter inutilement le volume et la complexité du code
      • Réduire le nombre de corrections à faire lors de la découverte d’un « bug » ou d’un problème de sécurité : une fonction générique et réutilisée n’est à modifier qu’à un seul endroit dans le code, et la correction s’applique à tout élément utilisant cette fonction
      • Éviter les différences de comportement entre réimplémentations d’une fonction qui devrait avoir le même comportement partout
    • Garder les privilèges nécessaires à une opération le moins longtemps possible

      Bonnes pratiques générales de développement

      L’application du principe de moindre privilège se fait également au niveau du code.

      Dans le cas où des privilèges élevés sont requis par l’application, accorder les privilèges le plus tard possible et les résilier immédiatement après la fin des opérations nécessitant ces privilèges.

    • Utiliser des outils d’analyse de code statique et dynamique durant les phases de développement

      Bonnes pratiques générales de développement

      Mettre en place un outil de suivi régulier de la qualité et de la sécurité du code afin d’assurer un suivi du niveau de tous les développements. Des outils comme SonarQube (qualité – intègre également quelques règles de sécurité) ou Checkmarx / IBM AppScan / HP Fortify (sécurité) permettent de s’interfacer avec l’environnement afin de fournir une analyse régulière du code et des statistiques au cours du temps. Ils permettent également de s’interfacer avec d’éventuels outils de qualité et de « ticketing » déjà en place.

      La mise en place de ces outils au sein de projets ayant déjà de gros volumes de codes étant difficile (car la quantité d’erreurs remontées lors des premiers passages est très importante), il est possible d’intégrer ces outils progressivement :

      • En passant les outils de qualité uniquement sur les nouveaux développements dans un premier temps, puis étendre progressivement à l’ensemble du code
      • En définissant un set minimal de règles de qualité et sécurité vérifiées par l’outil sur l’ensemble du code, puis ajouter progressivement des règles
    • Énumérer / Catégoriser les points d’entrée et les sources des données

      Validation des entrées utilisateurs

      Énumérer, puis catégoriser les sources de données selon la confiance portée à la validité des données qu’elles envoient. Ces sources peuvent être par exemple :

      • Une entrée utilisateur via un formulaire
      • Des données extraites d’une base de données
      • Le contenu d’un fichier
      • Des données envoyées par un autre service ou une autre application
      • Etc.
    • Vérifier les entrées côté serveur

      Validation des entrées utilisateurs

      Lorsque des données sont envoyées depuis un navigateur vers un serveur, les vérifications doivent être effectuées côté serveur.

      Les vérifications côté client ne sont pas suffisantes, car elles sont systématiquement contournables. L’utilisation de Proxy Web comme Burp Suite ou encore OWASP ZAP permettent de réaliser ce type d’opération très facilement.

    • Vérifier et « nettoyer » toutes les données en entrée

      Validation des entrées utilisateurs

      Partir du principe que les données externes fournies en entrée ne sont pas de confiance.

      Cela s’applique à tout type d’entrée, et tout type de donnée :

      • Entrées textuelles des utilisateurs via des formulaires, arguments
      • Requêtes réseau (pouvant être manipulées suite à une attaque de type « Man In The Middle »)
      • Données provenant d’un composant externe (ce dernier ne valide peut-être pas les données qu’il reçoit avant de les transmettre à une application tierce)
      • Fichiers, archives, données de configurations, variables d’environnement, etc.
      • Format spécifique de données (XML, JSON, etc.)
    • Privilégier l’utilisation des listes blanches pour la validation des entrées

      Validation des entrées utilisateurs

      Accepter ce qui est autorisé plutôt que de ne refuser que ce qui est explicitement interdit.

      Cela permet d’éviter les problèmes liés à la validation par liste noire, qui consiste à autoriser tout ce qui n’est pas explicitement interdit : il arrive régulièrement que des cas interdits soient oubliés, pas anticipés, ou pas suffisamment restrictifs ce qui permet de les contourner.

    • Utiliser des requêtes paramétrées et typées

      Validation des entrées utilisateurs

      L’objectif est d’éviter les injections dans des requêtes SQL, HQL, OSQL ou NOSQL en préformatant les requêtes afin que les entrées utilisateurs soient insérées dans la requête sans en altérer son exécution.

      Des bibliothèques, souvent intégrées au langage, existent pour le formatage de requêtes paramétrées comme par exemple :

      • PreparedStatement en Java
      • Parameters en C#

      Note : La création de requêtes SQL, HQL, OSQL ou NOSQL en concaténant des requêtes avec des entrées utilisateurs est une mauvaise pratique de sécurité fréquente, et à l’origine de la plupart des attaques permettant des injections de code SQL. Ce type d’injection de code peut permettre à un attaquant d’extraire le contenu de la base de données d’une application.

    • Filtrer les entrées pour n’accepter que des caractères autorisés

      Validation des entrées utilisateurs

      Si des caractères potentiellement dangereux doivent être acceptés (eg. <, /, ‘, “, etc.), s’assurer que des contrôles additionnels sont faits avant utilisation, et que ces entrées ne peuvent pas être exécutées en les échappant par exemple.

    • Accepter seulement le téléversement (« l’upload ») des types de fichiers nécessaires pour les objectifs métiers

      Validation des entrées utilisateurs

      Refuser tout type de fichier qui n’est pas explicitement autorisé.

      Par exemple, si le système « d’upload » de fichier est destiné à téléverser des documents administratifs scannés par les utilisateurs, alors il est possible de restreindre les formats autorisés aux PDF et à certains formats d’images.

    • Renommer les fichiers téléversés ou les stocker sous la forme d’un « blob » (Binary Large OBject)

      Validation des entrées utilisateurs

      Les fichiers nouvellement « uploadés » doivent être renommés avant d’être stockés sur le serveur. La routine de téléversement doit générer le nom de fichier (valeur aléatoire, MD5, etc.) et l’extension de ce dernier doit être supprimée ou « forcée » par l’application.

      Il est également possible de stocker les fichiers « uploadés » sous la forme d’un blob en base de données.

    • Limiter le nombre et la taille des fichiers téléversés

      Validation des entrées utilisateurs

      Cela permettra d’éviter qu’un attaquant provoque un déni de service en saturant l’espace disque de votre serveur d’application via « l’upload » de plusieurs fichiers de grandes tailles.

    • Retirer les droits d’exécution sur les répertoires qui reçoivent des fichiers « uploadés »

      Validation des entrées utilisateurs

      Cela permet d’éviter qu’un attaquant accède à un fichier nouvellement « uploadé » pour exécuter son contenu a posteriori.

    • Analyser les fichiers uploadés par l’utilisateur avec un antivirus

      Validation des entrées utilisateurs

      L’objectif est de vérifier qu’ils ne contiennent pas de code malveillant (eg. virus, malware).

      Par exemple, un fichier PDF peut contenir un code malveillant permettant d’exploiter une faille dans un lecteur PDF.

    • Ne pas passer de données, fournies par l’utilisateur via les fichiers « uploadés », à une fonction d’inclusion dynamique

      Validation des entrées utilisateurs

      Ce type d’inclusion peut permettre à un attaquant d’injecter du code dans une page ou dans une fonctionnalité afin d’en modifier ses caractéristiques ou d’exécuter du code malveillant sur le client.

      Exemple : la fonction « include » en PHP.

    • Ne jamais envoyer le chemin absolu vers un fichier sur le serveur à un client

      Validation des entrées utilisateurs

      Cette information pourrait aider un attaquant à accéder au fichier a posteriori.

    • Valider les cibles des redirections

      Validation des entrées utilisateurs

      Utiliser une liste blanche d’adresses ou de domaines autorisés.

      Cela permet d’éviter que les redirections qui sont supposées être effectuées vers des adresses de confiance ne soient modifiées afin d’être redirigées sur des sites malveillants, ou vers des pages non autorisées.

    • Encoder les données retournées au client en fonction du contexte d’affichage

      Validation des sorties utilisateurs

      Encoder toutes les données soumises par les utilisateurs, en fonction du contexte dans lequel ils vont être affichés, afin de s’assurer que celles-ci ne puissent pas être interprétées par les navigateurs Internet.

      Exemple : Après encodage javascript <script> devient javascript &lt;script&gt; et peut être affiché en toute sécurité dans un contexte de sortie HTML.

      De plus, il est recommandé de définir l’entête HTTP « X-XSS-Protection ». Cet en-tête permet d’activer les filtres anti-XSS incorporés dans les versions récentes des navigateurs Internet Explorer, Google Chrome et Safari.

    • Utiliser une routine standard et testée pour chaque encodage de sortie

      Validation des sorties utilisateurs

      Centraliser l’échappement des sorties pour avoir une routine commune et robuste.

      Il est possible d’utiliser des briques applicatives robustes et déjà éprouvées comme :

    • Tous les contrôles d’authentification doivent se dérouler sur un système de confiance

      Authentification

      Typiquement, sur le serveur d’application ou en se connectant à un serveur d’authentification distant.

    • Mettre en place et utiliser des services d’authentification standards et testés autant que possible

      Authentification

      Cela permet de se reposer sur une base éprouvée, réputée, sûre, et surtout maintenue et corrigée lors de la découverte de potentielles vulnérabilités.

    • Spécifier une implémentation centralisée pour effectuer tous les contrôles d’authentification

      Authentification

      Celle-ci inclut les bibliothèques faisant appel à des services d’authentification externalisés, et permet d’avoir un seul système de contrôle d’authentification à maintenir.

    • Les mots de passe ne doivent jamais être affichés en clair sur l’écran de l’utilisateur

      Authentification

      Ne pas afficher de retour à l’utilisateur lors de la saisie d’un mot de passe. Il est préférable d’utiliser des formulaires de type « Password » afin que celui-ci ne soit pas affiché.

    • Utiliser une authentification forte pour les comptes manipulant des informations très sensibles ou de grandes valeurs

      Authentification

      C’est-à-dire des authentifications multifacteurs telles que les OTP logiciels (eg. Google Authenticator), les OTP matériels (eg. tokens RSA), cartes à puce, etc.

      Note : Les OTP qui se basent sur un OTP reçu par SMS ne sont plus considérés comme sûrs, car des attaques qui ciblent les réseaux mobiles sont apparues. Celles-ci permettent d’intercepter les SMS et donc de lire l’OTP. Même si ces dernières sont complexes à mettre en œuvre, il existe un risque.

    • Valider les informations d’authentification seulement après que toutes les données nécessaires aient été reçues

      Authentification

      Ne pas faire de validation progressive, au risque d’indiquer à un attaquant quels critères sont valides. Par exemple, en validant chaque caractère d’un identifiant un par un et en stoppant à la première erreur rencontrée.

    • Les messages d’erreur rapportant les échecs d’authentification ne doivent pas indiquer quelle(s) partie(s) des informations d’authentification est incorrecte

      Authentification

      Cela peut aider un attaquant lors d’une attaque : en ayant la connaissance de la validité d’un identifiant lorsqu’il tente de s’authentifier avec, il peut effectuer une énumération des utilisateurs sur l’application. Dans un second temps, il peut tenter de découvrir les mots de passe des utilisateurs existants découverts précédemment.

      Il convient de configurer la fonctionnalité d’authentification pour ne pas donner d’information. Pour cela un message générique peut être affiché à l’utilisateur, par exemple « Identifiant et/ou mot de passe incorrect ».

    • N’utiliser que la méthode POST pour transmettre des informations d’authentification

      Authentification

      Les paramètres doivent être transmis dans le corps de la requête HTTP et jamais dans l’URL.

      Cela évite que les URL soient conservées dans les historiques et les journaux avec des informations d’authentification.

    • Imposer un délai d’attente ou un CAPTCHA après plusieurs tentatives successives d’authentifications échouées

      Authentification

      Afin de découvrir un mot de passe lié à un identifiant particulier, un attaquant peut avoir recours à une attaque par dictionnaire ou une attaque par « brute-force ». Une attaque par dictionnaire consiste à essayer successivement les mots de passe « courants » (de telles listes se trouvent très facilement sur internet).

      Si aucun mécanisme nécessitant une intervention humaine n’est nécessaire à la connexion ou à la soumission du formulaire, un attaquant peut réaliser très rapidement, et de manière automatisée, de très nombreuses tentatives de connexion.

      Pour réduire les possibilités d’attaques automatisées, il est possible de :

      • Mettre en œuvre un mécanisme de CAPTCHAs (http://www.captcha.net), après 3 tentatives infructueuses ayant pour objectif d’imposer une intervention humaine avant chaque soumission de formulaire. Une fois l’authentification réussie, ou un temps limite, le compteur repasse à 0 et le CAPTCHA est désactivé sur le compte.
      • Bloquer temporairement le compte. Le délai doit décourager les attaques par énumération de possibilités, mais être suffisamment court pour éviter les attaques par déni de service.
    • Le mécanisme d’authentification doit échouer de manière sécurisée

      Authentification

      L’échec d’une fonction de sécurité ne doit pas divulguer d’informations techniques ou permettre de contourner les mécanismes de sécurité de l’application.

    • Imposer les exigences de sécurité des mots de passe (en restant en accord avec la politique de sécurité)

      Gestion des comptes

      La stratégie de mots de passe regroupe les règles que doivent respecter les mots de passe définis de l’ensemble des comptes. La longueur et les caractères constituant le mot de passe sont deux éléments fondamentaux permettant de réduire les risques de compromission d’un compte utilisateur.

      Un mot de passe est considéré comme faible s’il peut être deviné facilement ou découvert par quelques essais successifs. C’est généralement le cas des mots de passe trop courts, n’utilisant que des lettres, que des chiffres ou utilisant un mot du dictionnaire.

      La composition des mots de passe doit être basée sur des principes de non-trivialité. Les mots de passe doivent répondre aux critères suivants :

      • Avoir une longueur d’aux minimum 8 caractères pour les utilisateurs et 14 pour les administrateurs ;
      • Être différents des noms d’utilisateurs et difficiles à deviner (éviter 0123456, 000000, date de naissance, etc.)
      • Comporter obligatoirement 3 classes de caractère parmi les suivantes : Minuscules, Majuscules, Chiffres et Caractères spéciaux
      • Ne pas être présent dans un dictionnaire de mots de passe faibles (prégénéré en fonction de la politique de mot de passe de l’application, de son nom, du nom de la société, etc.)
      • Imposer une durée de vie maximale au mot de passe afin d’obliger les utilisateurs à changer leur mot de passe (tous les 6 mois par exemple) est également une bonne pratique
    • La réinitialisation et la modification du mot de passe nécessitent le même niveau de contrôle que lors de la création d’un compte et de l’authentification

      Gestion des comptes

      L’utilisateur doit prouver son identité. Pour la modification du mot de passe, cela passe par la saisie du mot de passe actuel de l’utilisateur.

      Note : Il est nécessaire de redemander la saisie du mot de passe actuel lors du changement de paramètres servant sa réinitialisation comme l’email ou encore le numéro de téléphone.

    • Les « questions secrètes » pour la réinitialisation du mot de passe doivent être suffisamment robustes pour résister aux réponses communes

      Gestion des comptes

      Il faut éviter les questions avec des possibilités de réponse trop limitées, ou dont la réponse est très commune.

    • Si la réinitialisation du mot de passe est basée sur l’adresse e-mail, n’envoyer qu’un lien de réinitialisation ou un mot de passe temporaire, et ce uniquement à l’adresse déjà enregistrée pour le compte concerné

      Gestion des comptes

      Le vrai mot de passe actuel ne doit surtout pas être envoyé en clair.

    • Les mots de passe temporaires et liens de réinitialisation du mot de passe doivent avoir un délai d’expiration court

      Gestion des comptes

      Les liens de réinitialisation ne doivent également être utilisés qu’une fois et leurs durées de vie doivent être courtes.

    • L’utilisateur ayant demandé une réinitialisation de son mot de passe doit être forcé de le modifier lors de sa prochaine connexion

      Gestion des comptes

      Cette recommandation s’applique, quelle que soit la méthode de réinitialisation : lien ou mot de passe temporaire.

    • Avertir l’utilisateur concerné lorsqu’une modification du mot de passe est faite

      Gestion des comptes

      Par exemple, en lui envoyant un email de confirmation de changement de mot de passe.

    • Imposer des modifications de mot de passe en fonction des exigences établies dans la politique de sécurité

      Gestion des comptes

      Les systèmes critiques doivent bénéficier d’un changement de mot de passe plus fréquent. La durée de validité des mots de passe doit être contrôlée administrativement.

    • Le système de réinitialisation de mot de passe ne doit pas donner d’indication sur l’existence d’un compte

      Gestion des comptes

      Un message générique doit être affiché à l’utilisateur, ne lui donnant pas d’indication sur l’existence du compte en question.

      Par exemple : « Si le compte existe, un lien de réinitialisation a été envoyé à votre adresse mail ».

    • L’application doit gérer la désactivation et la suppression de comptes et mettre fin aux sessions courantes lorsque les autorisations sont révoquées

      Gestion des comptes

      Par exemple, pour les changements de rôle, l’expiration du mot de passe du compte, etc.

    • La création des identifiants de session ne doit se réaliser que sur un système de confiance

      Gestion des sessions

      Ce système de confiance n’est jamais implémenté côté client, mais toujours côté serveur.

    • La procédure de déconnexion doit être accessible sur toutes les pages nécessitant d’être authentifié

      Gestion des sessions

      Un utilisateur doit avoir la possibilité de facilement se déconnecter à tout moment et ne doit pas avoir à changer de page ou à retourner dans un menu spécifique pour mettre fin à la session.

    • Interdire la connexion permanente à une session et établir une durée d’inactivité au bout de laquelle la session expire

      Gestion des sessions

      Même si la session doit rester toujours active, la connexion doit expirer ou, à minima, demander à être renouvelée.

      Cette durée doit être aussi courte que possible tout en prenant en compte les besoins fonctionnels métiers et le risque lié à cette activité.

    • Générer un nouvel identifiant de session chaque fois qu’un utilisateur s’authentifie à nouveau

      Gestion des sessions

      Les identifiants de session ne doivent pas être réutilisés, même lorsqu’il s’agit du même utilisateur. Cela permet d’éviter les attaques permettant la « fixation de sessions ».

      Les identifiants de session doivent être renouvelés :

      • Dans le cas d’une nouvelle authentification
      • Lorsqu’il se réauthentifie après que sa session ait expiré
      • Etc.
    • Ne pas sauvegarder les identifiants de session dans les messages d’erreur ou les « logs »

      Gestion des sessions

      Un identifiant de session valide pourrait être récupéré dans les messages d’erreur ou dans les journaux, et être utilisé par un attaquant dans le but d’usurper l’identité de la victime.

    • N’utiliser que les mécanismes de contrôle de sessions fournis par le langage de programmation ou le Framework utilisé

      Gestion des sessions

      L’application doit considérer que seuls les identifiants de session générés avec des mécanismes de gestion de la session robustes et sûrs sont valides.

      Les mécanismes fournis par les langages de programmation ou Frameworks reconnus disposent généralement d’un niveau de fiabilité et de sécurité suffisant, ont fait l’objet de nombreux tests et sont couramment utilisés. Dans le cas où une vulnérabilité est découverte, cela garantit qu’elle est prise en charge et corrigée facilement.

      Par exemple, le mécanisme de gestion de contexte HTTP du langage Java (eg. JSESSSIONID) peut être utilisé.

    • Le domaine et le chemin doivent être spécifiés sur les cookies contenant des identifiants de session, et être les plus restrictifs possible

      Gestion des sessions
      L’objectif est de limiter la portée de réutilisation de ces cookies.
    • Assigner les attributs « HttpOnly » et « Secure » aux cookies

      Gestion des sessions

      L’attribut « HttpOnly » permet de spécifier au navigateur que le cookie ne sera pas accessible depuis les scripts exécutés côté client (eg. JavaScript). Activé, cet attribut constitue une première ligne de défense contre les vols de session exploitant les failles de type Cross-Site Scripting (XSS).

      L’attribut « Secure » doit, quant à lui, être activé lorsque le site est accessible en HTTPS, il permet de s’assurer que le cookie sera toujours transmis sur un canal sécurisé par SSL/TLS. Le cookie ne circulera donc jamais en clair sur le réseau, constituant ainsi une protection contre les vols de session par écoute du trafic (ex. : avec un logiciel du type Wireshark).

      Note : Ces attributs doivent être définis dans le code source de vos applications. Ceux-ci peuvent être très simples à définir (eg. il suffit de paramétrer 2 balises / variables à « true » sur Java / .NET ).

    • Ne pas exposer les identifiants de session dans l’URL

      Gestion des sessions

      Ceux-ci doivent être transmis uniquement par le biais de cookies dans l’entête HTTP.

      Ajouter le jeton de session à l’URL des pages du site Web l’expose de manière inutile. En effet, un attaquant pourrait le récupérer par un des biais suivants :

      • En accédant aux « logs » d’un éventuel proxy utilisé par la victime : ils comportent usuellement l’URL de la page visitée
      • Via une vulnérabilité de type Cross-Site Scripting (XSS) : du code JavaScript pourra récupérer le jeton de session, que ce soit en accédant à l’URL de la page visitée ou en analysant le code source d’une page le comportant

      Une fois le jeton de session récupéré, l’attaquant sera en mesure de voler la session d’un utilisateur et d’avoir accès à l’application avec ses droits.

    • Utiliser un composant unique pour vérifier les autorisations d’accès

      Contrôle d’accès utilisateur

      Un mécanisme dédié et idéalement séparé du reste du code applicatif doit effectuer les validations d’autorisation d’accès. Cela inclut également les bibliothèques qui font appel à des services externes d’autorisation.

      Des implémentations de vérifications différentes peuvent créer des problèmes d’inégalité entre les validations effectuées : certaines peuvent être incomplètes.

    • Vérifier, pour chaque action demandée, les droits requis pour faire l’action, et vérifier que l’utilisateur possède bien ces droits

      Contrôle d’accès utilisateur

      Cela inclut :

      • L’accès à une ressource (eg. fichiers, pages, etc.)
      • L’utilisation d’une fonctionnalité (eg. permet d’éviter les escalades de privilèges)

      Pour cela, il est nécessaire de réaliser les actions suivantes :

      • Faire l’inventaire exhaustif des différents rôles des utilisateurs devant utiliser l’application
      • Faire l’inventaire exhaustif des différentes actions que propose l’application
      • Construire la matrice en conséquence
      • Pour chaque action, vérifier en premier lieu que l’utilisateur est bien connecté, puis vérifier s’il possède bien le droit sur l’action

      Ces vérifications doivent être réalisées systématiquement par le serveur d’application.

    • Les contrôles d'accès doivent échouer de façon sécurisée

      Contrôle d’accès utilisateur

      Cela ne doit pas permettre à un utilisateur d’accéder à une autre ressource protégée par contrôle d’accès, par exemple en effectuant une redirection dangereuse.

    • Ne pas inventer ou réimplémenter d’algorithmes cryptographiques

      Cryptographie et protection des données

      Il est recommandé d’utiliser des implémentations reconnues et éprouvées par de nombreux utilisateurs. Par exemple, les Frameworks et les bibliothèques de certains langages intègrent directement des fonctions de génération d’aléa sécurisé, de chiffrement, de signature, etc.

      Dans le cas du Framework .NET, par exemple, la bibliothèque System.Security.Cryptography peut être utilisée.

    • Les modules cryptographiques utilisés par l’application doivent respecter la norme FIPS 140-2

      Cryptographie et protection des données

      La norme FIPS 140-2 définit des exigences de sécurité pour le choix des modules cryptographiques et propose une liste de modules, d’algorithmes, d’implémentation et de générateur d’aléa sécurisés approuvés selon leur domaine d’application.

      Actuellement, les algorithmes de sécurité approuvés sont les suivants, mais il convient de valider régulièrement cette liste afin de s’assurer que l’un de ces algorithmes n’est pas devenu vulnérable et n’est plus approuvé par la norme :

      • Chiffrement symétrique : AES, Triple-DES
      • Chiffrement Asymétrique : DSA, RSA, ECDSA
      • Hash (condensat) : SHA (version supérieure à SHA-1)

      La liste complète des modules cryptographiques approuvés est disponible ici :

    • Protéger les secrets cryptographiques des accès non autorisés

      Cryptographie et protection des données

      Ces secrets peuvent être des mots de passe, des clés privées, des certificats, etc. Cette protection signifie que les secrets cryptographiques ne doivent pas pouvoir être récupérés côté client par un attaquant lorsqu’ils sont stockés sur un serveur Web.

      Les secrets de type mots de passe, par exemple, peuvent être protégés en suivant la bonne pratique suivante :

      • Aucun mot de passe ne doit être retrouvé dans le code source de vos applications - Préférez le stockage de ce dernier dans un fichier de configuration, dans un « keystore » (eg. Trousseau de clés) ou dans un HSM (eg. Hardware Security Module)

      Les données sensibles nécessaires aux fonctionnements des applications ne doivent jamais être stockées dans le code source de vos applications.

    • Ne pas stocker de données sensibles en clair (ou dans un autre format qui ne soit pas sécurisé d’un point de vue cryptographique)

      Cryptographie et protection des données

      Les données sensibles comprennent les identifiants de connexion, les mots de passe, etc. Les formats non sécurisés comprennent les codes compilés de type Flash, Silverlight, etc.

    • Si l’application contient des informations d’authentification, elle doit s’assurer que les mots de passe stockés sont protégés en étant « Hashés » correctement

      Cryptographie et protection des données

      Lorsqu’une application est en charge de stocker des mots de passe, tout doit être mis en place afin de garantir leurs sécurités quoiqu’il arrive. Cela passe notamment par le stockage de ces mots de passe dans un format cryptographique qui ne permet pas de retrouver les mots de passe en clair, mais qui permet de valider leurs valeurs pour permettre une authentification par exemple.

      Il est possible, pour cela, d’utiliser une fonction particulière qui, à partir d’une donnée fournie en entrée (eg. mot de passe), calcule un condensat servant à identifier rapidement, la donnée initiale. Cette fonction ne peut être calculée que dans un sens. Il n’est donc pas possible de retrouver le mot de passe qui a permis de générer un condensat. Le condensat est appelé « Hash » et la fonction de génération de condensat est appelée fonction de « Hashage ».

      Afin de renforcer davantage la sécurité de la fonction de « Hashage », il est conseillé d’utiliser une valeur de sel (paramètre connu et unique pour chaque utilisateur que l’on va concaténer à un mot de passe avant la génération d’un « Hash ») à intégrer lors de la génération du « Hash ». Ce sel va permettre de protéger vos « Hash » contre les attaques qui se basent sur la création de tables arc-en-ciel (rainbow tables). Ces tables se basent principalement sur un dictionnaire de mots de passe pour lister un grand nombre de correspondances « Hash » / mot de passe. Il suffit de chercher un « Hash » dans cette liste pour retrouver son équivalent en clair (format non « Hashé »).

      Des fonctions comme MD5 et plus récemment SHA-1, sont reconnues comme présentant des faiblesses du point de vue de la sécurité et sont donc à prohiber. L’utilisation de fonctions de dérivations de clés spécifiquement conçues pour assurer le stockage sécurisé de mots de passe est recommandée (ex : BCrypt, SCrypt, PBKDF2, etc.).

    • Les fonctions cryptographiques doivent échouer de manière sécurisée

      Cryptographie et protection des données

      L’échec d’une fonction de sécurité ne doit pas divulguer d’informations techniques ou permettre de contourner les mécanismes de sécurité de l’application.

    • Utiliser une connexion chiffrée pour l’accès à tout contenu nécessitant une authentification ou toute information sensible

      Cryptographie et protection des données

      L’objectif est de protéger la confidentialité et l’intégrité de la donnée en transit.

      Il est fortement recommandé de n’utiliser que le protocole HTTPS et de ne plus exposer d’application via le protocole HTTP. En plus d’ajouter de la sécurité, l’application Web qui n’utilise que ce protocole sera mieux référencée par Google.

    • Utiliser une unique implémentation qui soit standard, correctement configurée et à jour pour le protocole TLS

      Cryptographie et protection des données

      La version de TLS doit être à jour et correctement configurée, c’est-à-dire qu’aucun des algorithmes proposés pour la signature et le chiffrement ne doivent être obsolètes :

      Durant la phase de négociation au début d’une communication via TLS, le client et le serveur négocient le système cryptographique qui sera utilisé pour la suite des échanges. Ce système regroupe l’algorithme de chiffrement asymétrique utilisé pour l’authentification, un algorithme de chiffrement symétrique utilisé pour le chiffrement des données, la clé de chiffrement associée, ainsi qu’une fonction de hachage utilisée pour le contrôle d’intégrité des échanges.

      Il est possible de tester la sécurité de sa configuration TLS via des outils comme :

    • Les certificats TLS utilisés doivent être valides

      Cryptographie et protection des données

      Cela implique qu’ils doivent :

      • Être en cours de validités
      • Être associés au bon nom de domaine
      • Ne pas être expirés ou révoqués
      • Être accompagnés des certificats intermédiaires lorsque cela est nécessaire
    • Empêcher les utilisateurs de générer ou modifier du code source

      Cryptographie et protection des données

      Les fonctionnalités offertes par l’application ne doivent pas permettre la génération ou la modification de code source.

    • Supprimer les données stockées via l’API de stockage Web dès lors qu’ils ne sont plus nécessaires

      Cryptographie et protection des données

      Il est nécessaire d’effacer les données stockées en « localStorage » et en « sessionStorage » dès lors qu’ils ne sont plus nécessaires pour limiter les possibilités de fuites d’information.

    • Désactiver la mise en cache côté client sur les pages contenant des informations sensibles

      Cryptographie et protection des données

      Il est possible de désactiver la mise en cache côté client en utilisant les directives « Cache-control : no-store » et « Pragma : no-cache ».

    • Ne pas dévoiler d’informations sensibles dans les messages et « logs » d’événements ou d’erreur

      Gestion des erreurs et journalisation

      Ces informations sensibles peuvent être :

      • des détails techniques du système d’exploitation et de l’application
      • des identifiants de session
      • des informations concernant le compte utilisateur
      • etc.
    • Implémenter des messages d’erreur génériques et des pages d’erreur personnalisées

      Gestion des erreurs et journalisation

      Les messages par défaut contiennent souvent des informations techniques précises utilisées à des fins de « debug », qui ne doivent pas être présentes en production.

    • Les messages d’erreur à destination des développeurs doivent être différents des messages à destination des utilisateurs

      Gestion des erreurs et journalisation

      Cela signifie que les messages d’erreur présents à des fins de « debug » dans les environnements de développement et de tests ne doivent pas persister en production.

      Des mesures doivent être prises pour s’assurer que la création d’une nouvelle « release » n’intègre pas d’informations de « debug ».

    • L’application doit prendre en charge les messages d’erreur et ne doit pas compter sur la configuration du serveur

      Gestion des erreurs et journalisation

      Ceux-ci contiennent généralement des détails techniques inutiles pour un utilisateur. Les erreurs affichées doivent donc provenir de l’application et non du serveur.

    • Lorsqu’une exception se produit, s’assurer que l’échec se produise de façon sécurisée

      Gestion des erreurs et journalisation

      Il doit exister une réponse adaptée à une erreur ou à un type d’erreurs : Arrêt sécurisé des processus, comportement et réponse aux utilisateurs, remontée d’alerte, etc.

    • Aucune erreur ne doit être dissimulée

      Gestion des erreurs et journalisation

      Les erreurs connues ou pouvant être anticipées doivent être gérées en conséquence, le reste des erreurs n’est pas attendu et doit être remonté. Il est possible de les gérer plus tard, en dernier recours, et à un endroit défini.

      Si l’on traduit cela en termes techniques, cela signifie que les clauses try/catch globales (sur toutes les exceptions) sont à proscrire, à l’exception d’une clause en dernier recours permettant de récupérer et de remonter spécifiquement toute erreur qui n’aurait pas été gérée avant.

    • Utiliser une routine centralisée pour chaque opération d’enregistrement dans le journal

      Gestion des erreurs et journalisation

      Un mécanisme uniforme de journalisation doit être utilisé pour s’assurer que les événements remontés sont enregistrés avec des informations suffisantes et cohérentes et stockées de manière sécurisée. Des bibliothèques intégrées ou externes de gestion des mécanismes de journalisation peuvent être utilisées. Par exemple :

      • Log4J (Java)
      • Log4Net (.NET)
    • Ne pas stocker d’informations sensibles dans les « logs »

      Gestion des erreurs et journalisation

      Les informations sensibles comprennent les détails techniques non nécessaires, les identifiants, les jetons de session, les mots de passe, etc.

      Les droits des fichiers de « logs » ne sont généralement pas les mêmes que ceux accordés aux fichiers contenant des informations sensibles.

    • S’assurer que les entrées du journal qui incluent des données ne provenant pas de sources de confiance sont correctement vérifiées avant d’être incluses dans les journaux

      Gestion des erreurs et journalisation

      L’objectif est de s’assurer qu’elles ne comportent pas de données pouvant altérer les journaux ou pouvant être exécutées.

      Un attaquant peut par exemple injecter des « logs », c’est-à-dire créer des « logs » falsifiés en exploitant le fait qu’une entrée utilisateur non validée sera incluse dans un « log ».

    • S’assurer que les journaux contiennent des informations précises pour chaque enregistrement

      Gestion des erreurs et journalisation

      Les journaux générés par vos applications doivent contenir les informations suivantes :

      • Un « timestamp » provenant d’un système de confiance
      • Un type/niveau d’événement (info, debug, warning, etc.)
      • Un tag/label permettant de différencier les événements relatifs à la sécurité lorsque ceux-ci sont mélangés avec les journaux d’événements techniques et fonctionnels
      • L’identité de la source (compte, utilisateur, équipement) à l’origine de l’événement
      • L’adresse IP source associée à l’événement
      • Le résultat de l’événement (succès, échec, etc.)
      • Une description de l’événement, ou un code permettant d’identifier de quel événement il s’agit
    • Enregistrer un set minimal d’événements de sécurité dans les journaux

      Gestion des erreurs et journalisation

      Les événements principaux à enregistrer sont :

      • Les échecs de validation des entrées
      • Les tentatives d’authentifications réussies et échouées
      • Les échecs de contrôle d’accès (par exemple : tentative d’accès à une page d’administration)
      • Les modifications de données inattendues
      • Les tentatives de connexion avec des identifiants de session invalides ou périmés
      • Les exceptions du système
      • Les opérations d’administration, y compris les changements apportés aux paramètres de sécurité
      • Les échecs et interruptions de connexion TLS
      • Les échecs des modules cryptographiques

    Déploiement

    • Octroyer les plus faibles privilèges possibles au serveur Web et aux processus

      Bonnes pratiques générales

      C’est-à-dire, avoir des privilèges restreints aux seules fonctions utilisées par ces composants.

      Cela reprend le principe de moindre privilège : les privilèges accordés doivent correspondre strictement à ceux nécessaires pour l’exécution correcte du serveur Web et des processus. Des privilèges plus élevés peuvent être accordés lorsque c’est nécessaire, mais uniquement durant le temps requis pour l’exécution des tâches concernées.

    • Limiter la fuite d’information dans vos bannières HTTP

      Bonnes pratiques générales

      Les informations disponibles dans l’entête HTTP « Server » ne doivent pas donner d’information sur le type ou la version de votre serveur d’application.

      Pour cela, il est nécessaire de revoir la configuration de votre serveur d’application pour limiter la fuite d’information dans vos bannières HTTP.

      Si un attaquant dispose du type et de la version de votre serveur d’application, celui-ci pourra rechercher s’il n’existe pas d’exploit connu qu’il pourrait utiliser pour compromettre votre périmètre applicatif. Les exploits connus peuvent facilement être trouvés avec des bases de données d’exploits publics comme :

    • Les comptes de service et les comptes supportant les connexions depuis (ou vers) l’extérieur doivent avoir le moins de privilèges possible

      Bonnes pratiques générales

      C’est-à-dire, avoir des privilèges restreints aux seules actions qu’ils doivent effectuer.

    • Restreindre l’accès aux journaux aux seules personnes autorisées

      Bonnes pratiques générales

      Les journaux doivent être protégés contre la lecture et surtout la modification non autorisées.

      Ceux-ci peuvent être utilisés à des fins de preuve légale dans le cas d’incidents, et leur intégrité doit donc pouvoir être vérifiée.

    • S’assurer que les fichiers et ressources de l’application sont en lecture seule

      Bonnes pratiques générales

      Cela permet d’empêcher toute altération de l’application et de son comportement.

    • Supprimer ou modifier les mots de passe par défaut des composants

      Bonnes pratiques générales

      Les mots de passe par défaut peuvent être trouvés dans les documentations des composants ou sur internet, et sont souvent les premiers testés lors de tentatives d’intrusion.

      Ils doivent être remplacés par des mots de passe robustes, suivant la politique de définition des mots de passe.

      Lorsque ces mots de passe sont associés à des comptes par défaut (type compte d’administration) qui ne sont pas utilisés, il est préférable de désactiver ces comptes inutiles.

    • Retirer toutes les fonctions et tous les fichiers qui ne seraient pas nécessaires au fonctionnement de l’application

      Bonnes pratiques générales

      Ces fichiers et ces fonctions inutiles sont souvent des exemples d’utilisation, des fonctions inutilisées, des pages de tests, des fichiers explicatifs ou des fichiers nécessaires à l’installation.

      Les fichiers et toutes les fonctionnalités qui n’ont pas de rôle à jouer dans l’environnement de production permettent d’agrandir la surface d’attaque. Par exemple, un attaquant peut accéder à une fonctionnalité (eg. « upload » de fichiers, page de « debug », formulaire de création de comptes, etc.) qui n’est pas nécessaire au fonctionnement de l’application pour l’aider dans ses objectifs de compromission.

    • Placer les dossiers qui ne sont pas publics dans des dossiers parents non accessibles depuis les applications Web

      Bonnes pratiques générales

      Cela a pour but d’empêcher la divulgation de fichiers et dossiers via l’arborescence, qui pourrait contenir des informations sensibles (informations techniques, mots de passe, informations d’utilisateurs, etc.).

    • Désactiver les fonctions de listing des dossiers

      Bonnes pratiques générales

      Cela permet d’éviter les attaques de type « directory listing » permettant à un attaquant d’accéder potentiellement à des fichiers non autorisés et sensibles.

      Il s’agit en général d’une configuration du serveur Web. Par exemple :

      • Options -Indexes sur Apache

    Maintenance

    • Définir une politique de mise à jour des composants externes utilisés par l’application

      Bonnes pratiques générales

      L’objectif est de garantir que les composants utilisés par l’application sont mis à jour et corrigés (via des correctifs de sécurité officiels) le plus rapidement possible sans causer de dysfonctionnement.

      De nouvelles vulnérabilités sont découvertes tous les jours sur les logiciels grand public. Des codes d’exploitation sont assez souvent développés puis mis en ligne sur Internet. Généralement, les éditeurs de logiciels sont assez réactifs et mettent à disposition des patchs corrigeant les vulnérabilités détectées.

      Il est possible de s’appuyer sur des outils afin de lister et vérifier automatiquement, pour toutes les dépendances d’une application, si une CVE (Common Vulnerabilities and Exposures) a été publiée pour la version utilisée :