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

Le États-Unis publication gouvernementale NISTIR 8397 : Instructions 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 obtenir la valeur la plus élevée dans chaque zone.

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 de développement de sécurité dynamique (SDL). Nous vous suggérons que pour votre produit dans son ensemble, pour une fonctionnalité spécifique ou pour une modification majeure de la conception ou de l’implémentation :

  • Disposez d’un SDL solide et dynamique qui permet un engagement précoce avec les équipes de développement et la mise en place de droits de l’approche.
  • Appliquez la modélisation des menaces de manière ciblée. Appliquez la modélisation des menaces à toutes les fonctionnalités, mais commencez tactiquement par des fonctionnalités exposées, complexes ou critiques. Appliquez-la régulièrement dans le cadre d’une révision de produit de haut en bas.
  • Appliquez 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 de stylet (test d’intrusion) ou des zones nécessitant des tests de sécurité tels que la 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 la 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/surutilisation, nous avons constaté que les concepts de base suivants doivent d’abord être traité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 à la production quotidiennement, il n’est pas pratique ou 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 appliquent. Par exemple :

  • La fonctionnalité apporte-t-elle un changement majeur dans la 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é autorise-t-elle les chargements de fichiers ? Dans ce cas, peut-être ce qui est plus approprié est une évaluation de sécurité des applications web.
  • Cette modification est-elle principalement une modification fonctionnelle de l’interface utilisateur ? Si c’est le cas, peut-être que rien n’est 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 des chronologie SDL de la fonctionnalité, afin qu’ils puissent collaborer à des moments appropriés.

Inventaire des produits

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

  • capteurs (tels que les rails de passagers et les véhicules),
  • réseaux basés sur un bus qui communiquent avec d’autres composants du véhicule (tels que CANBUS ou PROFCUBE),
  • sans fil/cellulaire/Bluetooth pour la communication avec les appareils clients et les back-ends cloud,
  • 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 fort des ressources vous permet d’afficher l’ensemble de la pile de produits pour voir l’image 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 métriques 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 des questionnaires de sécurité en phase requise, des résultats de modélisation des menaces, des résultats d’évaluation de la sécurité et des résultats à partir d’outils automatisés. Les combiner vous permet d’automatiser un point de vue relatif 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 Test automatisé

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’elles sont associées à d’autres pratiques de codage sécurisées, elles 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 régulièrement des tests automatisés et dans autant d’environnements que possible, pour vous assurer qu’ils sont exécutés et qu’ils couvrent tous les domaines :

  • La première fois qu’ils doivent s’exécuter se trouve sur l’ordinateur qui apporte les modifications. L’exécution de tests est plus facilement effectuée dans l’IDE utilisée pour la modification, ou en tant que script sur la ligne de commande, car le développeur apporte les modifications.
  • Le prochain endroit où ils doivent s’exécuter est dans le cadre du processus de validation/fusion de la demande de tirage.
  • Le dernier endroit où exécuter des tests fait partie d’un pipeline d’intégration continue et de déploiement continu (CI/CD) ou sur vos builds de candidats à la publication.

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 continues

La fiabilité des tests est une partie importante de la maintenance de l’efficacité de la suite de tests. Les échecs de test doivent être attribués et examinés, avec des problèmes de sécurité potentiels qui obtiennent une priorité élevée et se mettez à jour dans un délai d’invite et prédéterminé. Ignorer les échecs de test ne doit pas être une pratique courante, mais doit nécessiter une justification et une approbation fortes. Les échecs de test en raison de problèmes au sein de la suite de tests lui-même doivent être traités de la même façon que d’autres défaillances, afin d’éviter une défaillance dans la couverture dans laquelle les problèmes de produit peuvent être manqués.

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 pour la correction. 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 ont 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

Visual Studio Test Explorer prend en charge en mode natif la plupart des frameworks de test C++ les plus populaires et propose des options d’installation d’extensions pour plus d’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 les tests d’intégration continue. Azure DevOps et GitHub Actions facilitent l’exécution des validations automatiquement et bloquent les case activée ins de code en cas d’échec d’une validation. L’application automatisée permet de s’assurer que tout le code en cours d’case activée est sécurisé en fonction de ces case activée plus rigoureuses exécutées. Azure Pipelines et validation de build Azure DevOps sont décrits ici :

2.3 Analyse basée sur le code ou statique

L’analyse de code statique/binaire récapitulative doit être activée par défaut pour être sécurisée par défaut. L’analyse statique analyse un programme pour les stratégies de sécurité et de sécurité requises au moment de sa génération, et non au moment de l’exécution lorsqu’un exploit peut se produire sur l’ordinateur du client. L’analyse statique peut analyser le programme sous forme de code source ou sous forme exécutable compilée.

Recommandations Microsoft vous recommande :

  • 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 de case activée in.
  • Incorporez une analyse statique dans des pipelines CI sous forme de test.
  • L’analyse statique par définition est fournie avec des faux positifs et préparez-vous à incorporer ce fait dans votre boucle de commentaires de qualité. Soyez rapide pour activer tous les avertissements à faible faux positif avant. Ensuite, soyez proactif pour augmenter progressivement le nombre de règles pour lesquelles votre base de code compile avertissement-propre lorsque vous ajoutez régulièrement d’autres règles qui signalent des bogues importants au détriment de faux positifs progressivement plus élevés (initialement, avant que la base de code ait été propre ed 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/cycle de développement suivante.

Les outils clés prennent en compte et utilisent 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 de sécurité et de fiabilité. Elle doit être activée dans l’ensemble du développement d’un programme C++ chronologie. 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 de lignes directrices de base C++, 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.
  • /W4et /WX doit être activé dans la mesure du possible, pour vous assurer que vous compilez votre code propre ly à 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 case activée, car les erreurs ne deviennent visibles qu’après que le serveur principal du compilateur effectue l’analyse interprodurale et l’incorporation.
  • L’analyse binaire BinSkim garantit que les projets permettent 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 garde et la 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.dllou .exe) produits pour ou consommés par vos programmes. Le Guide de l’utilisateur BinSkim inclut une 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 la résolution des problèmes 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 le code source et l’analyse statique 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 intercepter les bogues sources dès que possible et réduire les coûts globaux. Les bogues au niveau binaire 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 (comme les builds nocturnes ou hebdomadaires).

2.4 Révision des secrets codés en dur

Résumé

Ne codez pas en dur les secrets au sein du 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 » signifient des entités qui établissent l’identité et fournissent l’accès aux ressources, ou utilisées pour signer ou chiffrer des données sensibles. Par exemple, citons les mots de passe, les clés de stockage, les chaîne 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 sont 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é) sont une vulnérabilité de sécurité. Voici des instructions générales sur la façon d’éviter les secrets dans le code source :

  • Utilisez un outil pré case activée in pour analyser et intercepter les secrets codés en dur potentiels dans le code avant de soumettre au contrôle de code source.
  • Ne placez pas d’informations d’identification de texte clair dans le code source ou les fichiers de configuration.
  • Ne stockez pas d’informations d’identification de texte clair dans SharePoint, OneNote, partages de fichiers, et ainsi de suite. Ou partagez-les par e-mail, messagerie instantanée, et ainsi de suite.
  • 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 de placer 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, production).

Bien qu’il ne soit pas directement lié aux secrets codés en dur, n’oubliez pas de sécuriser les secrets pour vos tests, développement et 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.
  • Ne donnez pas à la raison courante du développeur que « mes informations d’identification de test ne créent pas de risque ». En pratique, ils font presque toujours.
  • Envisagez de s’éloigner des secrets (par exemple, les mots de passe, les clés du porteur) entièrement en préférence des solutions RBAC/basées sur les identités comme une bonne solution d’ingénierie qui peut désaffecter entièrement le secret.

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, en fuite involontairement des secrets. Pour découvrir les secrets qui peuvent être masqués dans votre code source, vous pouvez utiliser des outils qui peuvent analyser votre code pour rechercher des secrets codés en dur :

Correction

Lorsque des informations d’identification sont trouvées dans votre code source, le besoin urgent immédiat est 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 en cours d’exécution, vous pouvez activer un gestionnaire de secrets pour la correction en procédant comme suit :

  1. Si la correction autorise le basculement vers des identités managées ou nécessite la suppression dans 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/PII, il peut nécessiter d’autres exigences de conformité/de 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 situation 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 les expositions secrètes en activant la protection Push sur leurs dépôts, en interceptant les expositions potentielles avant qu’ils ne soient jamais divulguées. 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 dépôts 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 dépôts 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, consultez :

Remarque

GitHub Advanced Security pour Azure DevOps apporte la même analyse secrète, 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 vos azure Repos et Pipelines.

Ressources supplémentaires

2.5 Exécuter avec les case activée et la protection fournies 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êcher les vulnérabilités exploitables dans le code,
  • activer les détections d’exécution qui déclenchent des défenses de sécurité sur l’exploitation et
  • activez la production et l’archivage des données pour limiter les dommages causés par les incidents de sécurité.

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

Microsoft fournit un ensemble d’installations spécifiques aux projets C++ pour aider les développeurs à écrire et à expédier 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 case activée binaire 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 utilisateur de 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 le temps de 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, et ainsi de suite. Les builds de mise en production doivent être ajustées à une combinaison appropriée de sécurité, de performances et d’autres problèmes de 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 à jour, 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 sur la mise à jour des outils est relativement élevé. L’utilisation de compilateurs obsolètes crée un risque particulier pour l’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, et ainsi de suite, qui réduisent les risques en favorisant la sécurité et la simplicité en C++, notamment :

  • Consultez la bibliothèque de prise en charge des recommandations de C++ Core (GSL) 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 l’implémentation de Microsoft GSL pour connaître les fonctions et les types que les instructions de base C++ vous suggèrent d’utiliser.
  • Conteneurs C++ de sécurité des ressources, protections de dépassement de mémoire de la bibliothèque runtime C (CRT) C : préférer std::vector et std::string, qui sont sécurisées par 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 Coffre Int 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 vulnérabilités de sécurité identifiées dans ces composants en mettant à jour vers des versions plus sécurisées lorsqu’elles sont soumises à 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 générer 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 sont en course pour évaluer et limiter les dommages lorsqu’un exploit se produit.
  • /SOURCELINK dans Visual C++ Linker - Inclure le fichier de liaison source dans PDB : le lien source 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 post-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 case activée du compilateur pertinents pour la sécurité comme des erreurs cassants, 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 opter pour les fonctionnalités de génération de code qui détectent et atténuent l’exécution de code malveillante, notamment :

Empêcher la divulgation d’informations sensibles

Les paramètres du compilateur doivent opter pour 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 qui proviennent de fonctionnalités matérielles telles que l’exécution spéculative.

Au niveau du logiciel, les données confidentielles peuvent être transmises aux attaquants si elles ont été divulguées de manière inattendue. L’échec de l’initialisation zéro des mémoires tampons et d’autres utilisations abusives de mémoire tampon peut fuiter des données confidentielles privées vers des attaquants qui appellent l’API approuvée. Cette classe de problème 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éculatif - Insère des instructions de barrière qui empêchent la divulgation de données sensibles produites par l’exécution spéculative. Ces atténuations doivent être activées pour le code qui stocke les données sensibles en mémoire et fonctionne sur 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 case activée d’exécution dans des blocs ou boucles critiques de performances. Ces chemins de code peuvent désactiver les atténuations via le spectre(nomitigation)declspec modificateur. Les projets qui activent « /Qspectre » doivent également être liés aux bibliothèques qui sont également compilées avec ces atténuations, y compris les bibliothèques d’exécution 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 sont au-delà des 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. Les cas d’erreur connus et les cas d’erreur sont utiles. La 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 rencontrer des incidents, des blocages, des interblocages, et ainsi de suite. En collectant des vidages sur incident dans le cadre des échecs de test, vous pouvez importer les vidages directement dans Visual Studio pour examiner plus en détail les parties du code qui rencontrent ces problèmes. 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ù dans la zone noire le test échoue et vous pouvez tester rapidement les correctifs.

Pour bien démarrer avec les tests de débogage, consultez Les 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 des plans de test. 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 du produit. Vous trouverez ici plus d’informations sur les plans de test Azure 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 tests basés sur le code sont faciles à exécuter 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 d’autres sections

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

  • tests unitaires,
  • tests paramétrables pour couvrir les fonctions avec plusieurs types d’entrée,
  • tests de composants pour séparer chaque composant de test et
  • test fictif 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

Grâce à ces tests, l’objectif est d’atteindre un niveau élevé de couverture des tests sur votre code. Vous devez suivre activement cette couverture et où existent les lacunes. À mesure que vous ajoutez d’autres tests qui exercicent 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 de ces tests fréquemment et reçoivent des commentaires sur les taux de réussite/échec et les emplacements d’échec rapidement. De nombreuses infrastructures de test prennent également en charge les fonctionnalités de CodeLens pour voir 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 aux groupes de tests, aux playlists de test personnalisées, au filtrage, au 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 affichent é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 les 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 resurfacer et d’augmenter 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 du code] et contribuer à la couverture globale du code du produit. En plus de cela, l’utilisation d’exemples réels de clients pour inspirer de nouveaux cas de test est un excellent moyen d’améliorer 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 :

Finalement, 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 appartiennent ensemble. Pour plus d’informations, consultez Exécuter des tests unitaires avec l’Explorateur de tests - Visual Studio (Windows)

2.9 Fuzzing

Le fuzzing résumé (également appelé test fuzz) 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é pour les exceptions telles que les blocages, l’échec des assertions de code intégrées ou du compilateur injectées et les fuites de mémoire potentielles.

Assistance

Utilisez une 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 nouvelle application et sa suite de test associée, incluez le flou pour les modules clés dès que possible. L’exécution d’une fuzzing pour la première fois sur un logiciel découvre presque toujours les vulnérabilités réelles qui étaient précédemment inconnues. Une fois que vous commencez à fuzzing, ne vous arrêtez jamais.

Relation avec d’autres sections

Lorsque le flou signale un échec, il fournit toujours naturellement 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 que le désinfecteur d’adresse (ASan) et la fuzzing :

  • Commencez par exécuter vos tests normaux avec des assainisseurs activés pour voir s’il y a des problèmes, une fois que le code est désinfecteur-propre commencer à 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 15 catégories d’erreurs de sécurité de mémoire avec zéro faux positifs. Pour C ou C++ lorsque vous avez une source, utilisez LibFuzzer, ce qui nécessite que ASan soit activé en premier.
  • Pour les bibliothèques écrites en Java, C#, Python, Rust, et ainsi de suite, utilisez l’infrastructure AFL++.

Qualités clés

  • L’analyse approximative détecte les vulnérabilités souvent manquées par l’analyse du programme statique, les tests de fonctionnalités exhaustifs et l’inspection manuelle du code.
  • La fuzzing est un moyen efficace de trouver des bogues de sécurité et de fiabilité dans les logiciels, tant que le cycle de vie du développement de la sécurité Microsoft nécessite une fuzzing à chaque interface non approuvée de chaque produit (voir également Threat Modeling).
  • Utilisez toujours la fuzzing pour les logiciels susceptibles de traiter des entrées non approuvées.
  • La 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 la fuzzing sur les 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 a déprécié ActiveX.
  • Lorsque le code est compilé sur Emscripten/WASM, il n’est plus C++ et d’autres outils s’appliquent.
  • Microsoft fournit RESTler, un flou d’API REST avec état.

Vue d’ensemble et qualités clés

Un scanneur d’applications web explore une application web en parcourant ses pages web et en l’examinant pour les 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. Critiquement, l’analyse des applications web doit couvrir/prendre en charge :

  • Catalogue toutes les applications web de votre réseau, y compris celles nouvelles et inconnues, et passe d’une poignée d’applications à des milliers d’applications.
  • Analyse approfondie des versions logicielles, des services et API REST SOAP et 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étection de programmes malveillants.

2.11 composants logiciels Point cluded

Résumé

Gérez votre code C++ identique au code écrit dans d’autres langages de programmation et appliquez les outils DCA (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 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/dépôt 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 un SBOM (facture de logiciels) avec votre produit listant toutes les dépendances telles que :

  • origine (par exemple, URL (Uniform Resource Locator))
  • version
  • cohérence (par exemple, hachage source SHA-256) et autres moyens de valider la cohérence, comme les builds déterministes.
  • Exiger et auditer des fichiers SBOM dans les dépendances logicielles ou produits dans le cadre d’une build incluant OSS (logiciel open source).
  • 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 génération 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 tierce 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 :