BLOG | BUREAU DU CTO

Au-delà du C

Publié le 07 février 2023

Pendant des décennies, C et ses descendants immédiats ont été les langages de programmation de choix pour la programmation système. Au début des années 1970, lorsque le langage C a été développé, il s’agissait d’une avancée importante par rapport au langage assembleur. Cinquante ans plus tard, nous pouvons faire mieux.

La sécurité informatique n’était pas sur le radar de la plupart des gens en 1970, et pendant de nombreuses années après. La première vulnérabilité majeure en matière de sécurité sur Internet s’est produite de nombreuses années plus tard, en 1988. Il était connu sous le nom de ver Internet et profitait d'un dépassement de mémoire tampon, où un tableau en mémoire était indexé en dehors de ses limites.  

Dans la famille de langages C, qui inclut C++, les tableaux ne sont pas soumis à des vérifications de limites. Il appartient au programmeur de s'assurer que l'accès au tableau est correct. Les erreurs de dépassement de mémoire tampon sont donc courantes. Pire encore, il est facile de provoquer délibérément un dépassement de mémoire tampon et ainsi d’accéder à une mémoire à laquelle on ne devrait pas accéder.

Le dépassement de tampon n’est qu’un exemple d’ insécurité de la mémoire . D'autres exemples connexes incluent l'arithmétique des pointeurs et les pointeurs suspendus (également appelés bogues d'utilisation après libération). Aujourd’hui, nous disposons de nombreux langages de programmation qui utilisent diverses techniques pour garantir que tout programme écrit dans ces langages sera exempt de problèmes de sécurité de la mémoire. La famille de langages C n’offre aucune garantie de ce type ; la sécurité de la mémoire n’a jamais été un objectif de conception de ces langages.

Environ 70 % des vulnérabilités de sécurité sont dues à des violations de la sécurité de la mémoire. Cette affirmation est appuyée par des données accablantes. La sécurité de la mémoire tient compte de :

  • 67 % des vulnérabilités exploitées ont été divulguées et détectées dans la nature (selon l'estimation de Google Project Zero 2021 ).
  • 90 % des vulnérabilités Android ( selon Google ). Bien que ce chiffre ait diminué récemment, grâce à l’utilisation de Rust, 89 % des vulnérabilités exploitables à distance restent liées à la sécurité de la mémoire.
  • 70 % des vulnérabilités Microsoft (selon Microsoft).
  • La majorité des correctifs de vulnérabilité Apple récents (sur Catalina , Big Sur , Monterey , Safari , iOS , tvOS , watchOS ).

En juillet 2022, 5/6 des vulnérabilités corrigées dans Chrome 103.0.5060.134 étaient des problèmes de sécurité de la mémoire. De toute évidence, éliminer les bogues de sécurité de la mémoire serait extrêmement utile. L’industrie sait comment faire cela depuis de nombreuses années : utiliser des langages de programmation sûrs en termes de mémoire. Historiquement, le problème a toujours été le coût associé en termes de performance. En raison de l’importance de la sécurité de la mémoire, les gens ont travaillé sur de nouvelles stratégies pour y parvenir sans les frais généraux traditionnels. Aujourd’hui, nous savons comment éliminer les erreurs de sécurité de la mémoire avec un coût d’exécution faible, voire nul. Les langages tels que Rust utilisent des innovations dans la conception des systèmes de types pour garantir la sécurité de la mémoire sans support d'exécution coûteux.

Cela conduit à une conclusion incontournable : arrêter d’écrire du nouveau code système en C/C++ (ou plus généralement, dans des langages non sécurisés en mémoire).

Il ne s’agit pas d’un appel à des réécritures massives et indiscriminées du code existant. Remplacer un logiciel existant est coûteux et non sans risques. Cependant, l’industrie doit cesser d’aggraver le problème en ajoutant davantage de code non sécurisé en mémoire aux bases de code existantes. Pour le code existant, privilégiez la réécriture des composants les plus sensibles : ceux qui sont responsables de la validation ou de la consommation d'entrées utilisateur non fiables, ceux exécutés dans un contexte privilégié, ceux exécutés en dehors d'un sandbox, etc.

Cette position, bien que largement répandue, reste controversée pour certains. Voici quelques-uns des arguments courants avancés en faveur du statu quo, ainsi que nos réponses à ces derniers.

  • Les bugs ne sont pas spécifiques aux langages de programmation.  
    • La plupart des vulnérabilités résultent de bugs de sécurité de la mémoire. Vous ne pouvez pas avoir de bugs de sécurité de mémoire dans des langages sécurisés en mémoire tels que Rust. Par conséquent, l’utilisation d’un langage sécurisé en mémoire empêchera la création de la plupart des vulnérabilités.
  • Les revues de code détecteront les bugs.
    • Des études montrent que même si les revues de code peuvent améliorer les choses, elles passent souvent à côté de nombreux problèmes ; elles ne trouvent certainement pas tout.
  • Les modules non sécurisés rendent tout le problème inutile.
    • Le code non sécurisé est explicitement délimité dans de très petites sections de code, de sorte que les ressources peuvent être concentrées sur sa validation.
  • Une implémentation d'un langage sécurisé en mémoire peut être boguée.
    • Certes, mais les probabilités sont bien plus faibles. Les compilateurs sont relativement petits et rigoureusement testés. Et réparer un compilateur corrigera automatiquement tous les programmes compilés avec lui, au lieu de corriger un bug dans un seul programme.
  • Seuls les programmeurs novices ou incompétents font ces erreurs.
    • Au contraire, les données ci-dessus sont tirées de projets de systèmes majeurs tels que le navigateur Chrome et les systèmes d’exploitation Windows et MacOS. Ces projets sont gérés par certains des développeurs les plus compétents et les plus expérimentés du secteur, et pourtant ils présentent toujours des problèmes de sécurité de la mémoire.
  • Ces problèmes peuvent être évités en suivant les meilleures pratiques.
    • Voir ci-dessus. Les équipes mentionnées suivent des pratiques très rigoureuses et utilisent les meilleurs outils disponibles.
  • Il n’est pas pratique de réécrire tout le code.
    • Personne ne plaide en faveur d’une réécriture complète de tout le code. Il est plutôt recommandé d’adopter une approche qui se concentre sur les éléments clés (privilèges élevés, grande surface d’attaque, éléments essentiels aux garanties de sécurité) et d’écrire du nouveau code dans un langage sûr en mémoire.
  • Les performances sont insuffisantes.
    • Les performances de Rust sont à peu près égales à celles de C/C++. Des différences mineures ne justifient pas les risques de sécurité. Il existe également des situations où les programmes Rust peuvent être plus rapides.
  • Les langages de programmation de systèmes à mémoire sécurisée comme Rust sont trop récents.
  • Il existe d’autres solutions telles que l’analyse statique, le fuzzing et le sandboxing.

Voir Quantifier l'insécurité de la mémoire et les réactions à celle-ci pour une discussion détaillée des points ci-dessus.

Malgré les objections, la dynamique en faveur des changements nécessaires s’accroît. D’importants investissements sont par exemple réalisés dans l’ Open Source Software Security Foundation (soutenue par la Linux Foundation). La sécurité de la mémoire a été discutée aux États-Unis Rapports du Sénat et de la NSAConsumer Reports s’efforce également d’identifier les différentes mesures incitatives qui pourraient accélérer ce mouvement en proposant une série de recommandations aux entreprises et aux agences gouvernementales.

Pour résumer :

L’importance de l’informatique pour la société a énormément augmenté au cours des cinquante dernières années. Le paysage des menaces pesant sur notre infrastructure informatique a également radicalement changé au cours des dernières décennies. Cependant, les langages de programmation que nous utilisons pour construire nos systèmes informatiques n’ont pas changé en conséquence. Le manque de sécurité de la mémoire est la principale source de vulnérabilités de sécurité dans les logiciels. Cela n’est pas propre à un type de logiciel spécifique, car partout où des langages non sécurisés en mémoire sont utilisés, les problèmes de sécurité de la mémoire abondent. Et en termes de chiffres, aucune autre classe de vulnérabilité ne s’en approche.

Nous disposons désormais des moyens nécessaires pour répondre à ce problème croissant et il est essentiel que notre industrie le fasse. Heureusement, la prise de conscience de la situation se répand, mais le problème est urgent et il n’y a pas de temps à perdre.