MSDN Magazine > Home > Issues > 2006 > November >  Pratiques sécuritaire: 8 règles simpl...
Pratiques sécuritaire
8 règles simples pour le développement d’un code mieux sécurisé
Michael Howard

Cet article aborde les sujets suivants:
  • Revue de votre code à l'aide d'experts et d'outils d'analyse
  • Réduction des risques à l'aide de la modélisation des menaces et du fuzzing
  • Méthode pour empêcher les données dangereuses d'intégrer vos applications
  • Apprentissage du plus grand nombre de concepts de sécurité possible
Cet article utilise les technologies suivantes:
Au fil du temps, j'ai eu la chance de travailler avec des milliers de développeurs de talent qui voulaient apprendre à écrire des logiciels qui soient mieux sécurisés. Au cours de ces années, j'ai également beaucoup appris de personnes très douées pour réaliser des systèmes sécurisés, et cela m'a donné une idée. Je me suis demandé si les "développeurs dans le domaine de la sécurité" partageaient des pratiques ou des compétences. Il s'avère que la réponse est un oui retentissant ! Cet article contient cette liste de pratiques partagées par les développeurs de code sécurisé.
Ce dont je suis sûr désormais est que toutes les personnes qui vont passer cette liste en revue vont immédiatement voir quelles sont les pratiques qui leur manquent. C'est une bonne chose. Je suis conscient qu'il existe d'autres bonnes idées de pratiques. Ceci n'est que ma liste ! Donc, cet élément mis à part, voici les pratiques les plus efficaces que j'ai pu observer au fil des années.

Pratique n°1 : Assumer la responsabilité
Ceci est une variante du commentaire "There is no silver bullet" (théorie de la balle en argent) énoncé par Fred Brookes dans The Mythical Man Month (le Mythe de l'homme x mois). La bonne sécurisation de vos produits dépend uniquement de vous. Personne d'autre—et encore moins un outil magique ou un langage de programmation—ne peut résoudre tous les problèmes de sécurité. Ne vous y trompez pas, j'apprécie les outils d'analyse du code source, mais ils ne règleront pas toutes vos failles de sécurité par magie. Vous seul pouvez le faire.
Les produits sécurisés sont conçus par des développeurs qui crées des concepts et écrivent des codes sécurisés. En fin de compte, écrire un code est un effort personnel. Vous êtes une personne, et vous ne pouvez pas être remplacé par un outil. Par conséquent, la sécurité de votre produit est votre responsabilité ! Les vers Blaster et CodeRed exploitaient un code écrit par des personnes (voir Figure 1).
Figure 1 Le code vulnérable est écrit par des personnes. 
Rappelez-vous que la totalité du code sera analysée minutieusement et risque d'être attaquée. Cela n'est pas un problème. C'est une bonne chose que d'être attaqué. La grande question est de savoir si votre code en sera fragilisé ? Vous seul pouvez déterminer si c'est ce qui va arriver. Aussi, soyez fier de votre code. Vous devez vous réjouir de la qualité de votre code et être capable de dormir la nuit en sachant que s'il est attaqué, vous avez fait tout votre possible pour empêcher ce code d'être piraté.
Si possible, faites examiner votre code par l'un de vos pairs, expert en sécurité. Ne le faites pas examiner par une personne qui ne s'y connaît pas en matière de sécurité et assurez-vous que cette personne recherche bien les bogues et les failles de sécurité. Faites tout votre possible pour trouver quelqu'un qui sache vraiment ce qu'il fait lorsqu'il regarde le code.
Et ne soyez pas prétentieux au point de ne pas pouvoir demander de l'aide lorsque vous en avez besoin. J'ai dit que vous ne deviez pas compter uniquement sur les outils, mais que vous devez absolument profiter au maximum de tous les éléments immédiatement disponibles. Servez-vous bien de tous les outils d'analyse du code source disponibles sur votre code, et utilisez-les souvent. Tirez parti de toutes les conceptions de langages de protection et de toutes les archives d'astuces disponibles. Par exemple dans C#, encapsulez-le code orienté réseau qui permet l'accès au tableau lorsque l'indexation du tableau se fait à partir d'une demande réseau dans des opérateurs activés, afin de détecter d'éventuelles erreurs de calcul d'entiers.

Pratique n°2 : Ne jamais se fier aux données
Je l'ai déjà dit un milliard de fois et je le répèterai encore : toute donnée d'entrée représente un danger jusqu'à preuve du contraire. Si vous étudiez les failles de sécurité les plus graves, vous verrez qu'elles ont toutes un point commun qui tient au fait que le développeur s'est fié aux données d'entrée. Que se passe-t-il si votre hypothèse s'avère incorrecte alors que votre code considère que les données sont bien formées ? Si vous êtes dans un bon jour, votre application échouera probablement. Si vous êtes dans un mauvais jour, l'attaquant risque d'injecter un code malveillant à votre processus et de le dévaster.
Une définition quelque peu fantaisiste d'un système de sécurité consisterait à dire qu'il effectue les tâches pour lesquelles il est conçu, et rien de plus. Mais en cas de problème lié à la fiabilité des entrées, le système peut généralement effectuer d'autres tâches. Une analyse superficielle des données présentées par Common Vulnerabilities and Exposures (expositions et failles les plus courantes - cve.mitre.org) montre qu'entre 2000 et 2004, 47% des failles de sécurité répertoriées par le CVE étaient dus à des problèmes de fiabilité des entrées. Les problèmes les plus connus sont dus à la saturation de la mémoire tampon, à des bogues de calcul d'entiers, aux scripts trans-sites et à des bogues de l'injection SQL. De nouvelles variantes apparaissent à ce sujet, telles que des failles lors de l'injection de XPath et de l'injection du protocole LDAP (Lightweight Directory Access Protocol).
Vous pouvez remédier à ces problèmes de fiabilité des entrées en suivant quelques règles simples. Tout d'abord, ne recherchez pas uniquement les éléments dont vous savez qu'ils sont dangereux ; ceci voudrait dire que vous connaissez tous les éléments dangereux et que vous pouvez prévoir quels seront les éléments dangereux à l'avenir. La recherche d'éléments dangereux est une bonne chose, du moment qu'elle ne constitue pas votre seul moyen de défense. Une meilleure stratégie consiste à limiter les entrées à celles dont vous savez qu'elles sont correctes. Pour y parvenir dans les langages de haut niveau tels que C# et Perl, j'aime utiliser des expressions régulières.
Ensuite, rejetez les éléments dont vous savez qu'ils sont incorrects. Par exemple, si quelqu'un à distance procède à une requête de fichier par votre code et que le nom du fichier contient un caractère douteux (tel que : ou \), rejetez cette requête. Et n'expliquez pas la raison de ce refus à l'attaquant ; dites simplement "fichier introuvable."
Enfin, nettoyez les données (ceci ne s'applique pas à tous les scénarios). Par exemple, dans le cas d'un serveur Web, il est conseillé de coder en HTML les données de sortie tirées de données d'entrée qui peuvent ne pas être fiables

Pratique n°3 : Modéliser les menaces à l'encontre de votre code
Vous disposez forcément de modèles de menaces ? Les modèles de menaces vous permettent de comprendre quels sont les risques potentiels qui menacent votre logiciel et de vous assurer que vous bénéficiez bien des solutions appropriées et que celles-ci sont bien en place. Mais l'intérêt de la modélisation des menaces ne s'arrête pas au simple concept de sécurisation. Les modèles de menaces peuvent également servir à améliorer la qualité de votre code. Les modèles de menace vous indiquent la provenance des données. Les données sont-elles fournies en local ou à distance ? Les données proviennent-elles d'utilisateurs anonymes ou d'utilisateurs fiables (authentifiés), comme peut-être les administrateurs ?
Grâce à ces informations, vous pouvez déterminer si vos protections sont appropriées. Par exemple, il vaut mieux bien sécuriser un code auquel des utilisateurs anonymes et à distance ont accès. Je ne dis pas qu'un code auquel seuls les administrateurs locaux ont accès ne doit pas être protégé ; ce que je veux dire, c'est qu'un code accessible à distance, en particulier un code exécuté par défaut, se doit d'être bien protégé, ce qui signifie plus de protections, plus de revue et plus d'attention portée aux détails. D'autre part, le modèle de menace peut vous informer de la nature des données sous protection. Par exemple, les données commerciales importantes et les informations d'identification personnelles doivent bénéficier d'un système de protection avancé. Vos protections sont-elles appropriées ?
Assurez-vous que les modèles de menace sont exacts et à jour, puis identifiez tous les points d'entrée dans votre code et classez-les par niveau d'accessibilité— utilisateurs à distance ou en local, avec droit d'accès élevé ou faible (ou interdits d'accès). Le code le plus facile d'accès devra être contrôlé en priorité et en profondeur. Enfin, passez en revue les cheminements de données anonymes tout au long du code ; en d'autres termes, suivez le cheminement des données depuis chaque point d'entrée accessible de façon anonyme afin de vérifier l'exactitude du code.

Pratique n°4 : Conserver une longueur d'avance
Le domaine de la sécurité évolue constamment. Il semble que chaque semaine, de nouvelles variantes des problèmes de sécurité apparaissent. Ceci signifie que vous devez évoluer et connaître les nouvelles menaces et systèmes de protection ou vous en subirez les conséquences.
Quelques stratégies simples existent pour conserver une longueur d'avance sur les nouveautés ; entre autre, veillez à lire quelques bons ouvrages sur la sécurité des logiciels de temps en temps. Veillez également à apprendre de vos erreurs passées ou mieux encore, des erreurs des autres. Pour cela, lisez le bugtraq (site consacré aux problèmes de sécurité) ; inscrivez-vous sur securityfocus.com pour recevoir les messages bugtraq dans votre boîte de réception. Mais surtout, suivez bien les conseils suivants : créez une règle dans votre boîte de réception afin de déplacer les messages vers un dossier spécial de façon à pouvoir gérer le volume entrant. C'est très important.

Pratique n°5 : Fuzzing
Le fuzzing est une technique de contrôle inventée pour rechercher les bogues de fiabilité. Il s'avère qu'un certain pourcentage des bogues de fiabilité sont des failles de sécurité attendant le bon exploit ! Bien sûr, une saturation de la mémoire tampon pourrait faire échouer l'application, mais si l'action malveillante d'un virus est bien conçue, l'échec ne se produit pas forcément, et l'attaquant peut alors exécuter le code et le diriger. Notre devise à nous c'est que"le refus de service aujourd'hui est l'exécution de code demain."
Pratiquement toutes les failles ou tous les bogues d'analyse de fichier ont été détectés par pur hasard ou par fuzzing. Microsoft a découvert des failles de sécurité en analysant un certain nombre de formats de fichiers dont les formats de fichier XLS, PPT, DOC, et BMP. La plupart des éditeurs ont été confrontés à des failles semblables ; ceci est dû au fait que l'analyse des structures de données complexes est une tâche difficile, que le code complexe contient des bogues et que certains de ces bogues révèlent des failles de sécurité.
Vous devez appliquer le fuzzing sur tout le code qui analyse le trafic réseau et des fichiers. Cycle de développement sécurisé (The Security Development Lifecycle) de Microsoft est très précis concernant ce que cela entraîne pour les formats de fichier. Vous devez appliquer le fuzzing à toutes les analyses contenant 100 000 itérations de fichiers malformés à l'aide d'un outil appelé « fuzzer » à utiliser sur des fichiers. Plusieurs fuzzers valables sont disponibles, et nous avons introduit un fuzzer pour fichiers ainsi qu'un code source C++ au livre The Security Development Lifecycle, dont Steve Lipner et moi-même sommes les co-auteurs (microsoft.com/MSPress/books/8753.asp).
Dernière remarque concernant le fuzzing. Si votre application échoue, ne pensez pas qu'il s'agit juste d'une panne. Il est probable qu'une bonne partie de ces pannes n'attendent plus qu'une chose : que quelqu'un écrive un exploit. Ne considérez donc plus un échec comme "un simple échec."

Pratique n°6 : Ne pas écrire de code non sécurisé
Chez Microsoft, nous utilisons le concept de barrières de qualité pour aider à réduire le risque pour un développeur d'activer un code vulnérable dans le produit. Les barrières utilisent une série d'outils d'analyse du code source sur le code avant d'enregistrer et de signaler un problème. Et tout problème identifié doit être réglé avant que l'enregistrement ne soit finalisé. Vous pouvez également appliquer des règles de code strictes, par exemple exclure l'utilisation des fonctionnalités interdites, comme l'interdiction des appels vers la fonction strcpy ou la fonction strncat et de cryptographies incorrectes. (Microsoft a interdit plus de 100 fonctions d'exécution C sur le code récent !) Par exemple, concernant la cryptographie, nous n'autorisons pas le DES (la longueur des clés n'est pas assez importante), le MD4 ou le MD5 (ils sont tous les deux dangereux) dans un code récent, sauf lorsque la norme nous y oblige.
Ne réinventez pas ces fonctionnalités. Si vous disposez d'un code qui analyse un format de fichier spécifique, vous n'avez pas besoin d'avoir deux ou trois ensembles de code d'analyse ; n'insérez qu'un ensemble, renforcez-le, et encapsulez-le dans un format qui puisse être utilisée sur plusieurs projets.
Enfin, rappelez-vous que les outils ne vous dispensent pas de connaître le mode d'écriture des codes de sécurité. C'est la raison pour laquelle il est si important d'être formés dans les domaines de la confidentialité et de la sécurité. Il faut que vous ayez une bonne compréhension des concepts pour pouvoir utiliser votre intuition et vos qualités de jugement, deux éléments que vos outils ne possèdent pas.

Pratique n°7 : Reconnaître l'asymétrie stratégique
C'est l'une de mes pratiques préférées. Rappelez-vous qu'en tant que développeur de logiciel, la chance en matière de sécurité n'est pas de votre côté. Je me plais à appeler cela "l'avantage de l'attaquant, et le dilemme du défenseur." Votre code et vos concepts doivent être 100% corrects tout le temps, ce qui est impossible. Pour ne rien arranger, vous devez atteindre cet objectif insurmontable en respectant un budget et un temps imparti, tout en devant prendre en compte les exigences en matière d'acceptabilité, de compatibilité, d'accessibilité et d'autres domaines en "-ité." Un attaquant peut passer autant de temps qu'il le souhaite dans la recherche d'un bogue, avant d'annoncer au monde que votre application n'est pas sécurisée.
Dans la pratique n° 6, j'ai mentionné le fait que vous ne deviez plus écrire de nouveau code qui ne soit pas sécurisé. Pour la pratique n°7, vous devez vous concentrer sur le code dans son ensemble, car les attaquants s'attaque à tout le code, quelle que soit son ancienneté. Consacrez du temps à la recherche de failles dans la partie ancienne du code et prenez sérieusement en compte les fonctionnalités non sécurisées, anciennes et obsolètes. Si vous employez des méthodes de développement souples, vous devez réfléchir au fait de consacrer un ou plusieurs sprints pour corriger la partie ancienne du code et l'adapter à la qualité de la partie du code la plus récente.

Pratique n°8 : Utiliser les meilleurs outils disponibles
Enfin, vous devez utiliser les meilleurs outils disponibles. J'apprécie les outils d'analyse du code source et j'apprécie toutes les technologies qui me permettent d'écrire un code mieux sécurisé. Comme je l'ai déjà dit, les outils ne sont pas la panacée mais ils peuvent aider. Et même être d'une grande aide ! Les outils peuvent également aider à réduire le problème de l'analyse du code source. Les outils peuvent analyser de grandes quantités de code rapidement, beaucoup plus vite qu'une personne ne le pourrait. Et ils vous permettent parfois d'évaluer combien un code peut être "incorrect".
L'une de mes astuces préférées consiste à compiler du code en utilisant le plus haut niveau d'alerte possible, par exemple /W4 lorsque vous utilisez Visual C++® ou –Wall lorsque vous utilisez gcc. Si un grand nombre d'alertes apparaissent dans le code, le code contient peut-être d'autres bogues qui n'ont pas été détectés par le dispositif de compilation ou les autres outils. Un tel code doit faire l'objet d'un examen approfondi avant d'être livré (voir pratique n°3).
Voici huit bonnes pratiques que des développeurs que je respecte profondément ont utilisé devant moi, au sein et en dehors de Microsoft. Ces pratiques en elles-mêmes ne feront pas de vous un brillant développeur dans le domaine de la sécurité, mais vous seront certainement d'un grand secours !

Michael Howard est l'un des responsables confirmés du programme de sécurité chez Microsoft et se concentre sur le processus de sécurité, sur l'amélioration de ce processus et sur l'application des méthodes recommandées. Il est le co-auteur de cinq ouvrages sur la sécurité dont The Security Development Lifecycle (le cycle de développement de la sécurité), Writing Secure Code (écriture d'un code sécurisé), et 19 Deadly Sins of Software Security (19 pêchés mortels dans le domaine de la sécurité des logiciels).

Page view tracker