Partager via


Créer des programmes C++ fiables et sécurisés

La publication gouvernementale des États-Unis NISTIR 8397 : Recommandations sur les normes minimales pour la vérification des logiciels pour les développeurs contient d’excellents conseils sur la façon de créer des logiciels fiables et sécurisés dans n’importe quel langage de programmation.

Ce document suit la même structure que NISTIR 8397. Chaque section :

  • résume l’utilisation des produits de développement Microsoft pour C++ et d’autres langages pour répondre aux besoins de sécurité de cette section et
  • fournit des conseils pour en tirer le meilleur parti dans chaque domaine.

2.1 Modélisation des menaces

Résumé

La modélisation des menaces est un processus précieux, en particulier lorsqu’elle est appliquée de manière à répondre à vos besoins de développement et qui réduit le bruit.

Recommandations

La modélisation des menaces doit faire partie de votre cycle de vie SDL (Security Development Lifecycle) dynamique. Nous vous suggérons, pour votre produit dans son ensemble, pour une fonctionnalité spécifique ou pour une modification majeure de la conception ou de l’implémentation de :

  • Disposer d’un SDL robuste et dynamique qui permet un engagement précoce avec les équipes de développement et le réajustement de l’approche.
  • Appliquer la modélisation des menaces de manière ciblée. Appliquer la modélisation des menaces à toutes les fonctionnalités, mais commencez tactiquement par des fonctionnalités exposées, complexes ou critiques. L’appliquer régulièrement dans le cadre d’une révision de produit de haut en bas.
  • Appliquer la modélisation des menaces dès le début (comme avec toutes les exigences de sécurité), quand il est toujours possible de modifier la conception. En outre, les modèles de menace servent d’entrée à d’autres processus, tels que la réduction de la surface d’attaque ou la conception pour la sécurité. Les modèles de menace créés ultérieurement sont au mieux des « enquêtes » pour les tests d’intrusion ou des zones nécessitant des tests de sécurité tels que le fuzzing. Une fois que vous avez créé un modèle de menace de référence, envisagez de continuer à l’itérer au fur et à mesure que la surface d’attaque change.
  • Utilisez l’inventaire des ressources et la conformité pour suivre de manière appropriée ce qui compose un produit et suivre les artefacts de sécurité (y compris les modèles de menace) ainsi que les ressources auxquels elles s’appliquent. Cette approche permet une meilleure évaluation automatisée des risques et la concentration des efforts de sécurité sur les composants ou fonctionnalités spécifiques qui changent.
  • Dans Azure, l’outil Microsoft Threat Modeling Tool a été mis à jour en 2022 pour le développement Azure. Pour plus d’informations, consultez Vue d’ensemble de Microsoft Threat Modeling Tool – Azure

Facteurs et pratiques de prise en charge

Pour appliquer correctement la modélisation des menaces et éviter les sous-utilisations/surutilisations, nous avons constaté que les concepts de base suivants doivent d’abord être abordés.

Approche de développement

Tout d’abord, comprenez l’approche de développement de l’équipe. Pour les équipes avec des flux de travail de développement agiles qui poussent des dizaines de modifications en production quotidiennement, il n’est pas pratique ni raisonnable d’exiger une mise à jour du modèle de menace pour chaque modification fonctionnelle. Au lieu de cela, au début de l’écriture des exigences fonctionnelles d’une fonctionnalité, envisagez d’inclure un questionnaire sur les exigences de sécurité. Le questionnaire doit se concentrer sur des questions spécifiques sur la fonctionnalité pour déterminer quels aspects futurs de votre SDL s’appliquent. Par exemple :

  • La fonctionnalité apporte-t-elle un changement majeur en matière de conception de la façon dont nous fournissons l’isolation des clients dans un environnement multilocataire ? Si c’est le cas, envisagez d’effectuer un modèle de menace complet.
  • Une nouvelle fonctionnalité permet-elle les chargements de fichiers ? Dans ce cas, une évaluation de sécurité des applications web peut être plus appropriée.
  • Cette modification est-elle principalement une modification fonctionnelle de l’interface utilisateur ? Si c’est le cas, il est possible que rien ne soit nécessaire au-delà de vos outils automatisés traditionnels.

Les résultats du questionnaire de sécurité informent les techniques SDL à lier à quelle unité de développement. Il informe également les partenaires de développement de la chronologie SDL de la fonctionnalité, afin qu’ils puissent collaborer aux moments appropriés.

Inventaire des produits

Ensuite, maintenez un inventaire solide des produits que vous êtes chargé d’évaluer. Les produits sont de plus en plus complexes. Il est courant d’écrire des logiciels pour les appareils connectés qui ont :

  • des capteurs (tels que les rails de passagers et les véhicules),
  • les réseaux basés sur un bus qui communiquent avec d’autres composants du véhicule (tels que CANBUS ou PROFIBUS),
  • le sans-fil/cellulaire/Bluetooth pour la communication avec les appareils clients et les back-ends cloud,
  • le Machine Learning dans le cloud qui alimente l’appareil ou une application de gestion de flotte,
  • et plus encore.

Dans ces produits complexes, la modélisation des menaces est essentielle. Le fait de disposer d’un inventaire robuste des ressources vous permet d’afficher l’ensemble de la pile de produits pour disposer d’une vision complète et de voir les emplacements clés qui doivent être évalués pour déterminer comment une fonctionnalité nouvelle ou modifiée a un impact sur la sécurité des produits.

Granularité et intégration

Établissez des systèmes pour mesurer la conformité à l’aide de mesures claires.

  • Mesurez régulièrement la conformité pour le développement au niveau des fonctionnalités. La conformité des fonctionnalités doit généralement être mesurée avec une fréquence plus élevée et une plus petite granularité, parfois même sur le système du développeur ou au moment de la validation/fusion du code.
  • Évaluez régulièrement la sécurité du produit plus large dans lequel une fonctionnalité ou un composant est consommé. Les évaluations plus larges sont généralement effectuées avec une fréquence plus faible et une granularité plus large, comme au moment du test du module ou du système.

Mise à l’échelle

Conservez un système d’inventaire des ressources approprié qui capture et conserve les artefacts de sécurité et la sortie des révisions de modèle de menace. Disposer d’un inventaire clair vous permet d’évaluer les sorties de révision pour les modèles et de prendre des décisions intelligentes sur la façon d’affiner régulièrement le programme de sécurité des produits.

Essayez de combiner les questionnaires de sécurité de la phase des exigences, les résultats de modélisation des menaces, les résultats d’évaluation de la sécurité et les résultats des outils automatisés. Les combiner vous permet d’automatiser un point de vue du risque relatif d’un produit donné, idéalement sous la forme d’un « tableau de bord », pour informer vos équipes de sécurité sur quoi se concentrer pour tirer le meilleur parti de la modélisation des menaces.

2.2 Tests automatisés

Résumé

Les tests automatisés constituent un moyen important de garantir la qualité et la sécurité de votre code. Il s’agit d’un élément intégral dans la prise en charge d’autres domaines mentionnés dans ce document, tels que la modélisation des menaces. Lorsqu’ils sont associés à d’autres pratiques de codage sécurisés, ils aident à se protéger contre les bogues et les vulnérabilités introduites dans la base de code.

Attributs de clé

Les tests doivent être fiables, cohérents et isolés. Ces tests doivent couvrir autant de code que possible. Toutes les nouvelles fonctionnalités et correctifs de bogues doivent avoir des tests correspondants pour garantir la sécurité et la fiabilité à long terme du code lorsque cela est possible. Exécutez des tests automatisés régulièrement et dans autant d’environnements que possible, pour vous assurer qu’ils s’exécutent et qu’ils couvrent tous les domaines :

  • Ils doivent d’abord s’exécuter sur l’ordinateur qui apporte les modifications. Il est plus facile d’exécuter les tests dans l’IDE utilisé pour la modification, ou en tant que script sur ligne de commande à mesure que le développeur apporte les modifications.
  • Ils doivent ensuite s’exécuter dans le cadre du processus de validation/fusion de la demande de tirage.
  • Le dernier endroit où exécuter des tests est un pipeline d’intégration continue et de déploiement continu (CI/CD) ou sur vos builds de version Release Candidate.

L’étendue des tests doit augmenter à chaque étape, la dernière étape fournissant une couverture complète pour tout ce que les autres étapes peuvent manquer.

Utilisation et maintenance en continu

La fiabilité des tests est un aspect important pour préserver l’efficacité de la suite de tests. Les échecs de test doivent être attribués et examinés, en donnant une priorité élevée aux problèmes de sécurité potentiels et en les mettant à jour dans un délai court et prédéterminé. Ignorer les échecs de test ne doit pas être une pratique courante, mais doit nécessiter une justification valable et une approbation. Les échecs de test en raison de problèmes au sein de la suite de tests elle-même doivent être traités de la même façon que d’autres défaillances, cela afin d’éviter un défaut de couverture qui empêcherait la détection des problèmes de produit.

Types de tests, en particulier les tests unitaires

Il existe plusieurs types de tests automatisés et, bien que tous ne soient pas applicables à toutes les applications, une bonne suite de tests contient une sélection de plusieurs types différents. Les cas de test basés sur le code, tels que les tests unitaires, sont les plus courants et les plus intégraux, étant applicables à toutes les applications et couvrant intentionnellement autant de chemins de code que possible. Ces tests doivent être petits, rapides et ne pas affecter l’état de la machine, afin que la suite complète de tests puisse être exécutée rapidement et souvent. Si possible, exécutez des tests sur de nombreux ordinateurs qui présentent des configurations matérielles différentes pour détecter les problèmes qui ne sont pas reproductibles sur un seul type de machine.

Visual Studio

L’Explorateur de tests Visual Studio prend en charge en mode natif la plupart des infrastructures de test C++ les plus populaires et propose des options d’installation d’extensions pour d’autres infrastructures. Cette flexibilité est utile pour exécuter un sous-ensemble de tests couvrant le code sur lequel vous travaillez et facilite le débogage des échecs de test à mesure qu’ils se produisent. Visual Studio facilite également la configuration de nouvelles suites de tests pour les projets existants et fournit des outils utiles tels que CodeLens pour faciliter la gestion de ces tests. Pour plus d’informations sur l’écriture, l’exécution et la gestion des tests C/C++ avec Visual Studio, consultez Écrire des tests unitaires pour C/C++ – Visual Studio (Windows).

Dans Azure et GitHub CI/CD

Les tests qui effectuent une vérification plus approfondie et prennent plus de temps à s’exécuter, tels que l’analyse statique, la détection des composants, etc., sont de bons candidats pour les tests de demande de tirage ou d’intégration continue. Les Actions Azure DevOps et GitHub facilitent l’exécution automatique des validations et bloquent les vérifications de code en cas d’échec d’une validation. L’application automatisée permet de s’assurer que tout le code archivé est sécurisé en fonction de ces vérifications plus rigoureuses. La validation de build Azure Pipelines et Azure DevOps est décrite ici :

2.3 Analyse basée sur le code ou statique

Résumé L’analyse de code statique/binaire doit être activée par défaut pour une sécurisation par défaut. L’analyse statique analyse un programme par rapport aux stratégies de sécurité requises au moment de sa génération, et non au moment de l’exécution lorsque du code malveillant exploitant une faille de sécurité peut se trouver sur l’ordinateur du client. L’analyse statique peut analyser le programme sous forme de code source ou d’exécutable compilé.

Recommandations Microsoft vous recommande de :

  • Activez l’analyse statique pour tous les programmes C++, à la fois pour le code source d’entrée (avant la compilation) et les fichiers binaires exécutables (après compilation). « Activer » peut signifier exécuter une analyse pendant chaque build sur l’ordinateur du développeur, ou en tant que build distincte pour inspecter le code ultérieurement ou en tant qu’exigence d’archivage.
  • Incorporez une analyse statique dans des pipelines CI sous forme de test.
  • L’analyse statique par définition produit des faux positifs. Préparez-vous à incorporer ce fait dans votre boucle de rétroaction de qualité. Activez rapidement tous les avertissements de faible faux positif au préalable. Ensuite, soyez proactif et augmentez progressivement le nombre de règles pour lesquelles votre base de code compile sans avertissement lorsque vous ajoutez régulièrement d’autres règles qui signalent les bogues importants au détriment de faux positifs progressivement plus élevés (initialement, avant que la base de code ait été nettoyée pour ces règles).
  • Utilisez toujours les dernières versions prises en charge de Visual Studio et configurez votre environnement d’ingénierie pour consommer rapidement les dernières versions des correctifs dès qu’elles sont disponibles, sans retarder l’étape/le cycle de développement suivant.

Outils clés Prenez en compte et utilisez les éléments suivants :

Remarques :

  • /analyze permet une analyse statique du code C++ au moment de la compilation pour identifier les vulnérabilités critiques du code en matière de sécurité et de fiabilité. Il doit être activé dans l’ensemble de la chronologie de développement d’un programme C++. Commencez par activer au moins « Microsoft Native Recommended » par défaut comme base de référence minimale. Consultez ensuite la documentation pour savoir comment spécifier d’autres règles, en particulier les règles C++ Core Guidelines, selon les besoins de vos stratégies d’ingénierie. La fonctionnalité Analyse statique du code source est disponible dans l’IDE Visual C++ et dans les outils de génération de ligne de commande.
  • /W4 et /WX doivent être activés dans la mesure du possible, pour vous assurer que vous compilez votre code correctement à des niveaux d’avertissement élevés (W4) et traitez les avertissements comme des erreurs qui doivent être corrigées (WX). Ces options permettent de rechercher des erreurs de données non initialisées que d’autres outils d’analyse statique ne peuvent pas vérifier, car elles ne deviennent visibles qu’après l’exécution de l’analyse interprocédurale et de l’incorporation par le back-end compilateur.
  • L’analyse binaire BinSkim garantit que les projets intègrent un large éventail de fonctionnalités de sécurité. BinSkim génère des PDB et d’autres sorties qui facilitent la vérification de la chaîne de responsabilité et une réponse efficace aux problèmes de sécurité. Microsoft recommande d’exécuter l’outil BinSkim pour analyser tous les fichiers binaires exécutables (.sys, .dll ou .exe) produits pour ou consommés par vos programmes. Le Guide de l’utilisateur BinSkim inclut la liste des normes de sécurité prises en charge. Microsoft vous recommande de résoudre tous les problèmes signalés comme des « erreurs » par l’outil BinSkim. Les problèmes signalés comme des « avertissements » doivent être évalués de manière sélective, car leur résolution peut avoir des implications sur les performances ou peut ne pas être nécessaire.

Dans Azure et GitHub CI/CD Microsoft recommande d’activer toujours l’analyse statique du code source et binaire dans les scénarios CI/CD de mise en production. Exécutez immédiatement l’analyse de la source sur l’ordinateur du développeur local, ou au moins pour chaque demande de validation ou de tirage, pour détecter les bogues sources dès que possible et réduire les coûts globaux. Les bogues au niveau des fichiers binaires ont tendance à être introduits plus lentement. Il peut donc être suffisant d’exécuter l’analyse binaire dans des scénarios CI/CD moins fréquents avant publication (comme les builds nocturnes ou hebdomadaires).

2.4 Révision à la recherche des secrets codés en dur

Résumé

Ne codez pas des secrets en dur dans le logiciel. Vous pouvez rechercher et supprimer efficacement les secrets du code source à l’aide d’outils fiables qui peuvent analyser l’intégralité de votre base de code source. Une fois que vous avez trouvé des secrets, déplacez-les vers un endroit sûr en suivant les instructions relatives au stockage sécurisé et à l’utilisation des secrets.

Problème

Les « secrets » sont des entités qui établissent l’identité et fournissent l’accès aux ressources, ou qui sont utilisées pour signer ou chiffrer des données sensibles. Par exemple, citons les mots de passe, les clés de stockage, les chaînes de connexion et les clés privées. Il est tentant de conserver des secrets dans le produit logiciel afin qu’ils puissent être facilement obtenus si nécessaire par le logiciel. Toutefois, ces secrets codés en dur peuvent entraîner des incidents de sécurité graves ou catastrophiques, car ils peuvent être facilement découverts et peuvent être utilisés pour compromettre votre service et vos données.

Prévention

Les secrets codés en dur dans le code source (sous forme de texte brut ou d’objet blob chiffré) constituent une faille de sécurité. Voici des instructions générales sur la façon d’éviter les secrets dans le code source :

  • Utilisez un outil de préarchivage pour analyser et détecter les secrets codés en dur potentiels dans le code avant de le soumettre au contrôle de code source.
  • N’insérez pas d’informations d’identification sous forme de texte clair dans le code source ni dans les fichiers de configuration.
  • Ne stockez pas d’informations d’identification de texte clair dans SharePoint, OneNote, partages de fichiers, etc. Ou partagez-les par e-mail, messagerie instantanée, etc.
  • Ne chiffrez pas un secret avec une clé de déchiffrement facilement détectable. Par exemple, ne stockez pas de fichier PFX avec un fichier qui contient son mot de passe.
  • Ne chiffrez pas un secret avec un déchiffrement faible. Par exemple, ne chiffrez pas un fichier PFX avec un mot de passe faible ou commun.
  • Évitez d’insérer des informations d’identification chiffrées dans le code source. Utilisez plutôt des espaces réservés dans votre source et laissez votre système de déploiement les remplacer par des secrets provenant de magasins approuvés.
  • Appliquez les mêmes principes aux secrets dans des environnements tels que les tests, la préproduction, etc. comme vous le faites dans les déploiements de production. Les adversaires ciblent souvent des systèmes hors production, car ils sont moins bien gérés, puis les utilisent pour pivoter en production.
  • Ne partagez pas de secrets entre les déploiements (par exemple, test, préproduction et production).

Bien que ce ne soit pas directement lié aux secrets codés en dur, n’oubliez pas de sécuriser les secrets pour les tests, le développement et la production :

  • Faites pivoter régulièrement vos secrets et chaque fois qu’ils auraient pu être exposés. Avoir une capacité démontrée à faire pivoter/redéployer des secrets est une preuve d’un système sécurisé. Plus particulièrement, l’absence de cette capacité est une preuve encore plus forte d’une vulnérabilité inévitable.
  • N’adoptez pas la pensée courante du développeur que « mes informations d’identification de test n’entraînent pas de risque ». Dans la pratique, elles en entraînent presque toujours.
  • Envisagez de vous éloigner entièrement des secrets (par exemple, les mots de passe, les clés du porteur) en préférant des solutions RBAC/basées sur les identités. Cela représente une bonne pratique d'ingénierie permettant d'éviter complètement la mauvaise gestion des secrets.

Détection

Les composants hérités de votre produit peuvent contenir des secrets masqués codés en dur dans leur code source. Parfois, les secrets des ordinateurs de bureau des développeurs peuvent s’infiltrer dans une branche distante et fusionner dans la branche release, laissant fuiter involontairement des secrets. Pour découvrir les secrets qui peuvent se cacher dans votre code source, vous pouvez utiliser des outils qui peuvent analyser votre code à la recherche de secrets codés en dur :

Correction

Lorsque des informations d’identification sont trouvées dans votre code source, il est impératif et urgent d’invalider la clé exposée et d’effectuer une analyse des risques en fonction de l’exposition. Même si votre système doit rester opérationnel, vous pouvez activer un gestionnaire de secrets pour y remédier en procédant comme suit :

  1. Si la correction autorise le basculement vers des identités managées ou nécessite l’ajout d’un gestionnaire de secrets tel qu’Azure Key Vault (AKV), effectuez cela en premier. Redéployez ensuite avec l’identité ou la clé mise à jour.
  2. Invalidez le secret exposé.
  3. Effectuez l’audit/l’évaluation des risques des dommages potentiels en raison d’une compromission.

Pour protéger les clés de chiffrement et d’autres secrets utilisés par les applications et services cloud, utilisez Azure Key Vault avec une stratégie d’accès appropriée.

Si une exposition compromet certaines données client/personnelles, elle peut nécessiter d’autres exigences de conformité/création de rapports.

Supprimez les secrets désormais invalidés de votre code source et remplacez-les par d’autres méthodes qui n’exposent pas les secrets directement dans votre code source. Recherchez des opportunités pour éliminer les secrets dans la mesure où cela est possible à l’aide d’outils comme Azure AD. Vous pouvez mettre à jour vos méthodes d’authentification pour tirer parti des identités managées via Azure Active Directory. Utilisez uniquement les magasins approuvés pour stocker et gérer des secrets tels qu’Azure Key Vault (AKV). Pour plus d’informations, consultez l’article suivant :

Azure DevOps (AzDO)

Les utilisateurs AzDO peuvent analyser leur code via GitHub Advanced Security pour Azure DevOps (GHAzDO). GHAzDO permet également aux utilisateurs d’empêcher l’exposition des secrets en activant la protection Push sur leurs référentiels, en détectant les expositions potentielles avant que les secrets ne fuitent. Pour plus d’informations sur la détection des secrets codés en dur dans le code dans Azure DevOps, consultez Analyse des secrets pour Github Advanced Security pour Azure DevOps dans chacun des liens suivants :

Dans GitHub

L’analyse des secrets est disponible sur GitHub.com sous deux formes :

  • Alertes d’analyse des secrets pour les partenaires. S’exécute automatiquement sur tous les dépôts publics. Toutes les chaînes qui correspondent aux modèles fournis par les partenaires d’analyse des secrets sont signalées directement au partenaire approprié.
  • Alertes d’analyse des secrets pour les utilisateurs. Vous pouvez activer et configurer une analyse supplémentaire des référentiels appartenant aux organisations qui utilisent GitHub Enterprise Cloud et qui disposent d’une licence pour GitHub Advanced Security. Ces outils prennent également en charge les référentiels privés et internes.

GitHub fournit des modèles connus de secrets pour les partenaires et les utilisateurs qui peuvent être configurés pour répondre à vos besoins. Pour plus d'informations, veuillez consulter :

Remarque

GitHub Advanced Security pour Azure DevOps fournit la même analyse des secrets, l’analyse des dépendances et les solutions d’analyse de code CodeQL déjà disponibles pour les utilisateurs GitHub et les intègre en mode natif dans Azure DevOps pour protéger votre Azure Repos et Pipelines.

Ressources supplémentaires

2.5 Exécuter avec les contrôles et la protection fournis par le langage et le système d’exploitation

Résumé

Le renforcement binaire est effectué en appliquant des contrôles de sécurité au moment de la compilation. Il s’agit notamment des atténuations qui :

  • empêchent les vulnérabilités exploitables dans le code,
  • activent les détections d’exécution qui déclenchent des défenses de sécurité en présence de code malveillant et
  • activent la production et l’archivage des données pour limiter les dommages causés par les incidents de sécurité.

Les consommateurs de fichiers binaires doivent opter pour les fonctionnalités de sécurité Windows pour bénéficier pleinement du renforcement.

Microsoft fournit un ensemble de fonctionnalités spécifiques aux projets C++ pour aider les développeurs à écrire et à livrer du code plus sûr et plus sécurisé. Les développeurs C++ doivent également respecter les normes de sécurité communes aux langages qui génèrent du code exécutable. Microsoft gère BinSkim, un vérificateur de fichiers binaires OSS public qui permet d’appliquer l’utilisation de nombreuses protections décrites dans cette section. Pour plus d’informations sur BinSkim, consultez le guide de l’utilisateur Binskim | GitHub

Les contrôles au niveau binaire diffèrent selon l’emplacement où ils sont appliqués dans le processus d’ingénierie. Vous devez faire la distinction entre les options du compilateur et de l’éditeur de liens qui sont strictement au moment de la compilation, modifier la génération de code avec une surcharge d’exécution et modifier la génération de code pour assurer la compatibilité avec les protections du système d’exploitation.

Les paramètres du développeur doivent préférer activer autant d’analyse statique que possible, permettre la production de données privées afin d’accélérer le débogage, etc. Les builds de mise en production doivent être ajustées en fonction d’une combinaison appropriée de sécurité, de performances et d’autres aspects de la génération de code. Les processus de mise en production doivent être configurés pour générer et gérer correctement les données de build publiques et les données de build consommées en privé (par exemple, les symboles publics et privés).

Restez à jour : utilisez toujours des compilateurs et des outils à jour

Compilez tout le code avec les ensembles d’outils actuels pour bénéficier de la prise en charge du langage actualisée, de l’analyse statique, de la génération de code et des contrôles de sécurité. Étant donné que les compilateurs affectent chaque composant généré, le risque de régression lors de la mise à jour des outils est relativement élevé. L’utilisation de compilateurs obsolètes crée un risque particulier d’action corrective lors de la réponse à un incident de sécurité, car les équipes peuvent ne pas avoir suffisamment de temps pour mettre à niveau les compilateurs. Microsoft recommande aux équipes de développer l’installation pour actualiser et tester régulièrement les mises à jour du compilateur.

Utiliser des méthodes de développement sécurisées, des versions de langage, des frameworks/API

Le code doit utiliser des méthodologies de développement, des versions de langage, du framework, des API, etc., qui réduisent les risques en favorisant la sécurité et la simplicité en C++, notamment :

  • Consultez Bibliothèque de prise en charge des recommandations (GSL) C++ Core Guidelines pour obtenir des conseils sur l’écriture de code C++ moderne, sécurisé et cohérent qui suit les meilleures pratiques et évite les pièges courants.
  • Consultez Implémentation des GSL de Microsoft pour connaître les fonctions et les types que les C++ Core Guidelines vous suggèrent d’utiliser.
  • Conteneurs C++ sécurisés pour les ressources, protections de dépassement de mémoire de la bibliothèque runtime C (CRT) : préférez std::vector et std::string, qui sont sécurisés pour les ressources. Si vous devez utiliser des données C, utilisez les versions sécurisées des fonctions CRT, qui sont conçues pour empêcher l’altération de la mémoire en raison de l’utilisation incorrecte des mémoires tampons et des comportements de langage non définis.
  • La bibliothèque SafeInt protège contre le dépassement d’entier dans les opérations mathématiques et de comparaison.

Consommer des dépendances sécurisées

Les fichiers binaires ne doivent pas être liés aux bibliothèques et dépendances non sécurisées. Les équipes de développement doivent suivre toutes les dépendances externes et résoudre les CVE/vulnérabilités de sécurité identifiées dans ces composants en mettant à jour vers des versions plus sécurisées en présence de ces vulnérabilités.

Optimiser les garanties de provenance du code et l’efficacité de la réponse de sécurité

La compilation doit permettre des garanties de provenance de code forte pour détecter et empêcher l’introduction de portes dérobées et d’autres codes malveillants. Les données résultantes, également essentielles au débogage et à l’examen, doivent être archivées pour toutes les versions logicielles afin de produire une réponse de sécurité efficace si elles sont compromises. Les commutateurs de compilateur suivants génèrent des informations essentielles à une réponse de sécurité :

  • /ZH:SHA_SHA256 dans Visual C++ : garantit qu’un algorithme sécurisé par chiffrement est utilisé pour générer tous les hachages de fichiers sources PDB.
  • /Zi, /ZI (format des informations de débogage) dans Visual C++ : en plus de publier des symboles supprimés pour collecter des données d’incident et d’autres scénarios d’utilisation publique, assurez-vous que les builds produisent et archivent des fichiers PDF privés pour tous les fichiers binaires publiés. Les outils d’analyse binaire nécessitent des symboles complets pour vérifier si de nombreuses atténuations de sécurité ont été activées au moment de la compilation. Les symboles privés sont essentiels dans la réponse à la sécurité, et réduisent les coûts de débogage et d’investigation lorsque les ingénieurs travaillent rapidement pour évaluer et limiter les dommages en présence d’un code malveillant exploitant une faille de sécurité.
  • /SOURCELINK dans l’éditeur de liens Visual C++ – Inclure le fichier de liaison source dans PDB : Source Link est un système indépendant de langage et de contrôle de code source fournissant le débogage source pour les fichiers binaires. Le débogage source augmente considérablement l’efficacité de la plage de validations de sécurité de préversion et de réponse aux incidents après mise en production.

Activer les erreurs du compilateur pour empêcher les problèmes au moment de la création du code

La compilation doit activer les vérifications du compilateur pertinentes pour la sécurité comme des erreurs cassantes, par exemple :

Marquer les fichiers binaires comme compatibles avec les atténuations de sécurité du runtime du système d’exploitation

Les paramètres du compilateur et de l’éditeur de liens doivent inclure les fonctionnalités de génération de code qui détectent et atténuent l’exécution de code malveillant, notamment :

Empêcher la divulgation d’informations sensibles

Les paramètres du compilateur doivent inclure la prévention de la découverte des informations sensibles. Ces dernières années, les chercheurs ont découvert des fuites d’informations involontaires provenant de fonctionnalités matérielles telles que l’exécution spéculative.

Au niveau des logiciels, des données confidentielles peuvent être transmises aux attaquants en cas de fuite inattendue. L’échec de l’initialisation à zéro des mémoires tampons et d’autres utilisations abusives de la mémoire tampon peuvent fuiter des données confidentielles privées auprès d’attaquants qui appellent l’API approuvée. Cette classe de problème est mieux gérée en activant une analyse statique supplémentaire et en utilisant des conteneurs de ressources sécurisés, comme décrit précédemment.

  • /Qspectre – Atténuer les attaques côté canal spéculatives – Insère des instructions barrières qui empêchent la divulgation de données sensibles produites par l’exécution spéculative. Vous devez activer ces atténuations pour le code qui stocke des données sensibles en mémoire et franchit une limite d’approbation. Microsoft recommande toujours de mesurer l’impact sur les performances par rapport aux benchmarks appropriés lors de l’activation des atténuations Spectre en raison de la possibilité d’introduire des vérifications d’exécution dans des blocs ou boucles critiques pour les performances. Ces chemins de code peuvent désactiver les atténuations via le modificateur spectre(nomitigation) declspec. Les projets qui activent /Qspectre doivent également être associés à des bibliothèques qui sont aussi compilées avec ces atténuations, y compris les bibliothèques runtime Microsoft.

2.6 Cas de test de boîte noire

Résumé

Les tests de boîte noire ne reposent pas sur la connaissance du fonctionnement interne du composant testé. Les tests de boîte noire sont conçus pour tester les fonctionnalités de bout en bout des fonctionnalités du produit à n’importe quel niveau ou couche. Les tests de boîte noire peuvent être des tests fonctionnels, des tests d’interface utilisateur, des tests de performances et des tests d’intégration. Les tests de boîte noire sont utiles pour mesurer la fiabilité générale et l’exactitude fonctionnelle, et s’assurer que le produit se comporte comme prévu.

Relation avec d’autres sections

Ces types de tests basés sur des exigences sont utiles pour valider les hypothèses faites dans le modèle de menace et couvrir les menaces potentielles comme indiqué dans cette section. Ces tests sont utiles pour tester l’intégration entre des composants distincts du produit, en particulier ceux qui franchissent les limites de confiance, comme décrit dans le modèle de menace. Les cas de test de boîte noire sont également utiles pour tester tous les types de cas de périphérie pour la validation d’entrée utilisateur. Il est tout aussi utile de tester les cas d'erreur périphériques connus que les cas d'erreur. Le fuzzing est également utile pour tester des cas moins évidents.

Automatisation et régression

Exécutez ces tests régulièrement et comparez les résultats aux exécutions précédentes pour intercepter les changements cassants ou les régressions de performances. En outre, l’exécution de ces tests sur de nombreux ordinateurs et configurations d’installation différents peut vous aider à couvrir les problèmes susceptibles de survenir à partir de différentes architectures ou modifications de configuration.

Vidages sur incident

Ces tests permettent de trouver des problèmes de fiabilité, de pouvoir tester de nombreux scénarios différents susceptibles de générer des incidents, des blocages, des interblocages, etc. En collectant les vidages sur incident dans le cadre des échecs de test, vous pouvez les importer directement dans Visual Studio pour examiner plus en détail les parties du code qui posent problème. Si vous exécutez des tests fonctionnels à partir de Visual Studio, vous pouvez facilement répliquer et déboguer des échecs en voyant exactement où le test échoue dans la boîte noire et vous pouvez tester rapidement les correctifs.

Pour bien démarrer avec les tests de débogage, consultez Tests unitaires de débogage avec l’Explorateur de tests – Visual Studio (Windows).

Dans Azure

Azure DevOps peut également aider à gérer et valider ces tests avec l’utilisation de Test Plans. Ces tests peuvent être utilisés pour garantir l’approbation avec la validation manuelle et exécuter des tests automatisés associés aux exigences produit. Vous trouverez ici plus d’informations sur Azure Test Plans et leur utilisation pour exécuter des tests automatisés :

2.7 Cas de test basés sur le code

Résumé

Les cas de test basés sur le code font partie intégrante du maintien de la sécurité et de la fiabilité de votre produit. Ces tests doivent être petits et rapides et ne doivent pas avoir d’impact les uns sur les autres afin qu’ils puissent être exécutés en parallèle. Les développeurs peuvent facilement exécuter ces tests localement sur leur ordinateur de développement chaque fois qu’ils apportent des modifications au code sans se soucier du ralentissement de leur cycle de développement.

Types et relation avec les autres sections

Les types courants de cas de test basés sur le code sont les suivants :

  • tests unitaires,
  • tests paramétrés pour couvrir les fonctions avec plusieurs types d’entrée,
  • tests de composants pour séparer chaque composant de test et
  • tests fictifs pour valider des parties du code qui communiquent avec d’autres services, sans étendre l’étendue du test pour inclure ces services eux-mêmes.

Ces tests sont basés sur le code interne écrit, tandis que les tests de boîte noire sont basés sur les exigences fonctionnelles externes du produit.

Objectif

À travers ces tests, l’objectif est d’atteindre un niveau élevé de couverture des tests sur votre code. Vous devez suivre activement cette couverture et la localisation des lacunes. À mesure que vous ajoutez d’autres tests qui parcourent davantage de chemins de code, la confiance globale dans la sécurité et la fiabilité de votre code augmentent.

Visual Studio

Les outils de l’Explorateur de tests dans Visual Studio facilitent l’exécution fréquente de ces tests et la réception rapide de commentaires sur les taux de réussite/d’échec et les emplacements d’échec. De nombreuses infrastructures de test prennent également en charge les fonctionnalités de CodeLens pour connaître l’état de test à l’emplacement du test lui-même, ce qui facilite l’ajout et la maintenance de la suite de tests. L’Explorateur de tests facilite également la gestion de ces tests, ce qui permet des groupes de tests, des playlists de tests personnalisées, le filtrage, le tri, la recherche, etc.

Pour plus d’informations, consultez l’article suivant :

Visual Studio est également fourni avec des outils pour le suivi de la couverture du code. Ces outils vous permettent de vous assurer que les modifications apportées au code que vous apportez sont couvertes par des tests existants ou pour ajouter de nouveaux tests pour couvrir les chemins de code nouveaux et non testés. Les outils indiquent également le pourcentage de couverture du code pour s’assurer qu’il est maintenu au-dessus d’un niveau cible pour garantir la confiance dans la qualité globale du code.

Pour plus d’informations sur ces outils, consultez Tests de couverture du code – Visual Studio (Windows).

Dans Azure

Azure DevOps peut également vous aider à suivre les résultats de couverture du code pour l’ensemble du produit dans le cadre du processus de pipeline de build. Pour plus d’informations, consultez Révision de la couverture du code – Azure Pipelines.

2.8 Cas de test historiques

Résumé

Les cas de test historiques, également appelés cas de test de régression, empêchent les anciens problèmes de refaire surface et augmentent la couverture globale des tests du produit. Vous devez vous assurer que lorsqu’un bogue est résolu, le projet ajoute également un cas de test correspondant. Au fil du temps, à mesure que des correctifs sont faits, la robustesse globale de la suite de tests continuera à s’améliorer, offrant ainsi de meilleures garanties de fiabilité et de sécurité.

Qualités clés et relation avec d’autres sections

Étant donné qu’ils testent les régressions de bogues, ces tests doivent être rapides et faciles à exécuter, afin qu’ils puissent s’exécuter en même temps que les cas de test basés sur le code et contribuer à la couverture globale du code du produit. En plus de cela, l’utilisation d’exemples réels de clients pour imaginer de nouveaux cas de test est un excellent moyen d’accroître la couverture et la qualité des tests.

Visual Studio

Visual Studio vous permet d’ajouter facilement des tests à la suite tout en apportant les modifications nécessaires pour corriger le bogue, et d’exécuter rapidement les tests et la couverture du code pour vous assurer que tous les nouveaux cas sont pris en compte. Référencer l’ID de bogue à partir de votre système de suivi des problèmes dans votre code où vous écrivez le test est un bon moyen de connecter des tests de régression aux problèmes correspondants. Préférez utiliser Azure Boards et les plans de test avec Visual Studio :

  • pour associer des tests, des cas de test et des problèmes ; et
  • pour suivre tous les aspects d’un problème et de ses tests correspondants.

Pour plus d’informations, consultez l’article suivant :

Enfin, l’intégration de ces tests dans la zone de test unitaire censée couvrir la section de code permet de maintenir la suite de tests organisée et plus facile à gérer. Vous pouvez utiliser le regroupement de tests de l’Explorateur de tests pour suivre efficacement les tests qui vont ensemble. Pour plus d’informations, consultez Exécuter des tests unitaires avec l’Explorateur de tests – Visual Studio (Windows).

2.9 Fuzzing

Résumé Le fuzzing (également appelé test à données aléatoires) est une technique de test logiciel automatisée qui implique la fourniture de données non valides, inattendues ou aléatoires comme entrée dans un programme. Le programme est ensuite surveillé à la recherche d’exceptions telles que des blocages, l’échec des assertions de code intégrées ou injectées par le compilateur et des fuites de mémoire potentielles.

Assistance

Utilisez le fuzzing sur tous les logiciels susceptibles de traiter des entrées non approuvées qu’un attaquant pourrait contrôler. Si vous créez une application et sa suite de test associée, incluez le fuzzing pour les modules clés dès que possible. La première exécution du fuzzing sur un logiciel détecte presque toujours des vulnérabilités qui étaient précédemment inconnues. Une fois que vous commencez le fuzzing, ne vous arrêtez jamais.

Relation avec d’autres sections

Lorsque le fuzzing signale un échec, il fournit toujours un cas de test reproductible qui illustre le bogue. Ce cas de test peut être reproduit, résolu, puis ajouté aux cas de test historiques.

Lors de l’utilisation des deux assainisseurs tels qu’Address Sanitizer (ASan) et le fuzzing :

  • Commencez par exécuter vos tests normaux avec les assainisseurs activés pour voir s’il y a des problèmes, puis une fois que le code est nettoyé, démarrez le fuzzing.
  • Pour C ou C++, il existe des compilateurs qui automatisent l’injection d’assertions d’exécution et de métadonnées qui activent ASan. Lorsqu’ils sont compilés pour ASan, les fichiers binaires résultants sont liés à une bibliothèque d’exécution qui peut diagnostiquer précisément plus de 15 catégories d’erreurs de sécurité de la mémoire sans faux positif. Pour C ou C++, lorsque vous avez une source, utilisez LibFuzzer, qui nécessite qu’ASan soit activé en premier.
  • Pour les bibliothèques écrites en Java, C#, Python, Rust, etc., utilisez l’infrastructure AFL++.

Qualités clés

  • Le fuzzing détecte les vulnérabilités souvent manquées par l’analyse de programme statique, les tests de fonctionnalités exhaustifs et l’inspection manuelle du code.
  • Le fuzzing est un moyen efficace de trouver des bogues de sécurité et de fiabilité dans les logiciels, tant est si bien que Microsoft Security Development Lifecycle nécessite le fuzzing à chaque interface non approuvée de chaque produit (voir également Modélisation des menaces).
  • Utilisez toujours le fuzzing pour les logiciels susceptibles de traiter des entrées non approuvées.
  • Le fuzzing est efficace pour les applications autonomes avec des analyseurs de données volumineux.

Azure et GitHub CI/CD

Modifiez votre ou vos builds pour prendre en charge la création continue d’exécutables qui utilisent LibFuzzer ou AFL++. Vous pouvez ajouter des ressources informatiques supplémentaires requises pour le fuzzing sur des services tels que OSS-Fuzz ou OneFuzz.

2.10 Analyse des applications web

Résumé

Dans l’étendue de Microsoft Visual C++ sur Windows, Microsoft recommande :

  • Préférez TypeScript, JavaScript et ASP.NET pour les applications web.
  • N’écrivez pas d’extensions web en C++. Microsoft déconseille ActiveX.
  • Lorsque le code est compilé sur Emscripten/WASM, il n’est plus C++ et d’autres outils s’appliquent.
  • Microsoft fournit RESTler, un fuzzer d’API REST avec état.

Vue d’ensemble et qualités clés

Un analyseur d’applications web explore une application web en parcourant ses pages web et en l’examinant à la recherche de vulnérabilités de sécurité. Cette analyse implique la génération automatique d’entrées malveillantes et l’évaluation des réponses de l’application. Il est essentiel que l’analyse des applications web couvre/prenne en charge les fonctionnalités suivantes :

  • Catalogage de toutes les applications web de votre réseau, y compris les nouvelles et inconnues, et puisse prendre en charge aussi bien quelques applications que des milliers.
  • Analyse approfondie des versions logicielles, et des services SOAP et API REST utilisés par les appareils mobiles.
  • Insertion de primitives de sécurité dans le développement et le déploiement d’applications dans les environnements DevOps. Ces primitives fonctionnent avec le robot d'indexation.
  • Détection de programmes malveillants.

2.11 Vérifier les composants logiciels inclus

Résumé

Gérez votre code C++ de la même façon que le code écrit dans d’autres langages de programmation et appliquez les outils SCA (Software Composition Analysis) et d’analyse d’origine (OA) adoptés par votre entreprise à votre code C++. Les flux de travail et l’analyse de sécurité doivent être conçus dans le cadre des systèmes CI/CD (intégration continue et livraison continue).

Défense en amont

Pour atténuer le risque d’attaques sur les dépendances en amont, les sources/composants tiers doivent être stockés sur une ressource contrôlée par l’entreprise, sur laquelle les outils SCA et OA sont exécutés.

  • Les outils doivent analyser et alerter quand des vulnérabilités sont identifiées (y compris les bases de données publiques) telles que : Accueil | CVE
  • Exécutez une analyse statique sur tous les composants logiciels inclus dans votre application/référentiel pour identifier les modèles de code vulnérables.

Défense des dépendances

Effectuez et gérez un audit des dépendances pour vérifier que toutes ces occurrences sont prises en compte et couvertes par vos outils SCA et OA.

  • Les composants doivent être régulièrement audités et mis à jour vers les dernières versions vérifiées.
  • Dépendances de flux de package.
  • Les outils SCA/OA couvrent et auditent toutes les dépendances de package provenant d’un seul flux.

SBOM

Produisez une nomenclature de logiciel (SBOM) avec votre produit répertoriant toutes les dépendances telles que :

  • l’origine (par exemple, l’URL (Uniform Resource Locator))
  • version
  • la cohérence (par exemple, le hachage source SHA-256) et autres moyens de valider la cohérence, comme les builds déterministes.
  • Exigez et auditez les fichiers SBOM dans les dépendances logicielles ou produits dans le cadre d’une build incluant un logiciel open source (OSS).
  • Microsoft standardise et recommande SPDX (Software Package Data Exchange) version 2.2 ou ultérieure | Linux Foundation comme format de document SBOM.
  • Le déterminisme de build peut être utilisé pour produire indépendamment des binaires identiques au niveau du bit et fournir des vérifications indépendantes de l’intégrité :
    • Attestation interne ou tierce de reproductibilité
    • D’autres techniques telles que la signature binaire via une source de certificat approuvée peuvent également fournir des garanties d’intégrité binaire.

Ressources supplémentaires

Les solutions Microsoft incluent les conseils et produits suivants :