Internet aurait de sérieux problèmes à cause de langages comme C et C++ favorisant la survenue de failles
Mais peu de développeurs s'en soucieraient

Le , par Christian Olivier, Chroniqueur Actualités
Un bogue affecte les iPhone, un autre touche Windows et un troisième affecte les serveurs tournant sous Linux. À première vue, ces bogues n’ont rien en commun puisqu’ils concernent des plateformes différentes : Android, iOS, macOS, Windows, Linux. La réalité serait cependant toute autre à en croire Alex Gaynor, ingénieur en sécurité logicielle chez Mozilla précédemment en poste chez USDS (United States Digital Service).

Lors de la troisième édition du « Weakest Link », le rendez-vous annuel organisé par Motherboard Vice sur le thème de l’avenir du piratage informatique et de la cybersécurité, Alex Gaynor a évoqué un problème sérieux qui, selon lui, menacerait Internet, mais qui semble paradoxalement laisser les développeurs complètement indifférents.

Gaynor a expliqué que les trois bogues évoqués précédemment existent parce que les logiciels qu’ils affectent sur les différentes plateformes ont été codés avec des langages de programmation qui ont la fâcheuse tendance de favoriser la survenue d’erreurs de type « memory unsafety », en autorisant l’accès aux régions invalides de la mémoire. Cette catégorie d’erreurs peut causer des bogues et des vulnérabilités de sécurité lors des procédures d’accès en mémoire.

En autorisant la survenue d’erreurs de type « memory unsafety », des langages de programmation tels que C et C++ auraient ainsi contribué à la dissémination d’un flux presque sans fin de failles de sécurité critiques pendant de nombreuses années. Comme exemple de ces vulnérabilités, on peut citer : les confusions de type, le dépassement de tampon, le dépassement de capacité d’entier et la vulnérabilité « use after free ».

Les confusions de type peuvent survenir lorsqu’une portion de code ne vérifie pas le type d’objet qui lui est passé et l’utilise à l’aveuglette. Ce genre de situation peut s’avérer dangereuse dans la mesure où un type est exprimé comme une disposition dans l’implémentation de bas niveau d’un logiciel. De plus, avec les confusions de type, les mauvais pointeurs sur les fonctions ou les mauvaises données sont assignés à la mauvaise portion du code, ce qui peut dans certains cas conduire à une exécution du code.

Le dépassement de tampon (ou buffer overflow en anglais) est une faille de sécurité critique qui se produit lorsque l’utilisateur entre une chaine de caractères qui va se retrouver dans un tableau de caractères de taille insuffisante. Cela entraine l’écriture de données en dehors de la zone mémoire allouée pour le tableau. L’exploit HeartBleed, par exemple, qui a eu un impact sur 17 % des serveurs Web sécurisés sur Internet, était une vulnérabilité de dépassement de tampon permettant de lire 60 Ko après la fin d’une liste, y compris les mots de passe et autres données des utilisateurs.

Le dépassement de capacité d’entier, une faille difficile à détecter qui utilise le fait que les nombres ne peuvent dépasser une certaine valeur qui dépend du nombre de bits utilisés pour leur représentation et de leur méthode de codage.

La vulnérabilité « use after free » se présente, en général, dans le cas où on utilise un pointeur ou des données en mémoire, alors que le pointeur (ou le bloc mémoire) a déjà été libéré.


Ensemble, ces vulnérabilités représenteraient les exploits les plus fréquemment rencontrés dans des logiciels populaires tels que Firefox, Chrome, Windows, Android ou encore iOS. Gaynor en dénombre déjà au moins 400 et affirme : « Je suis les avis de sécurité pour ces projets depuis plus d’un an et, dans presque toutes les versions de ces produits, plus de la moitié des vulnérabilités sont de type memory unsafety. Plus troublant encore, les vulnérabilités sévères et critiques […] sont presque toujours de type memory unsafety ».

Malgré les risques importants liés à la sécurité qu’ils font planer en permanence sur les logiciels qu’ils soutiennent, les langages de programmation « memory unsafety friendly » comme le C ou le C++ ont toujours la côte auprès des développeurs, alors que des alternatives éprouvées telles que Rust, Swift, pouvant être considérées comme des langages « memory safe » semblent à la traine.

Cette situation serait peut-être due au fait que, dans le cadre d’un nouveau projet, les développeurs ont tendance à opter pour un langage de programmation en fonction des langages que leur équipe connait, des performances et de l’écosystème de bibliothèques qui peuvent découler de ce choix. Le volet sécurité qu’implique ce choix n’est presque jamais considéré, ou du moins pas suffisamment, lors de la prise de décision estime Gaynor.

De plus, la plupart des projets logiciels, même les plus importants pour la sécurité d’Internet, ne sont pas nouveaux. Ils ont été lancés il y a une décennie ou plus. Linux, OpenSSL et le serveur Web Apache par exemple ont tous plus de vingt ans. Pour des projets d’envergure comme ceux-ci, réécrire tout le code dans un nouveau langage n’est pas une option. Ils ont besoin d’être transférés progressivement, ce qui implique que les projets devront être écrits et maintenus dans deux langages différents au lieu d’un seul. Cela suppose également qu’il faudra former une grosse équipe, ce qui prend beaucoup de temps et nécessite plus de moyens.

Le plus gros problème serait enfin lié au fait que beaucoup de développeurs ne croient pas du tout qu’il y a un problème. Ils ne croient pas que les langages comme C ou C++ facilitent ces vulnérabilités, mais que ce sont simplement d’autres ingénieurs qui écrivent du code bogué. Ils ne pensent pas qu’il y aurait un quelconque problème avec ces langages prétendument « memory unsafety friendly », car aucun code n’est parfait, mais simplement des personnes qui ne savent pas correctement les utiliser.

Source : Motherboard Vice

Et vous ?

Qu’en pensez-vous ?

Quel est ou quels sont vos langages de prédilection ?

Partagez-vous la théorie des langages « memory unsafety friendly » comme C ou C++ mise en avant dans cet article ? Expliquez votre point de vue.

Êtes-vous d’accord avec Gaynor lorsqu’il déclare que le volet sécurité est trop souvent négligé au moment du choix d’un langage ?

Voir aussi

Les pièges du C
Google publie des détails sur une faille non corrigée d'IE et Microsoft Edge qui a été signalée en fin novembre
C2 : un langage qui se présente comme une évolution de C plus rapide, sans fichiers d'en-tête, avec système de build intégré et d'autres changements
Pourquoi les langages C et C++ auraient-ils encore de nombreuses années devant eux ? Donnez votre avis


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse Signaler un problème

Avatar de koyosama koyosama - Membre éprouvé https://www.developpez.com
le 16/11/2018 à 12:47
Je pense c'est juste penible de reinventer la roue. Refaire tout en Rust. Sans compter que les developpeurs avec les capacites pour developper ce genre d'appalication est rare de nos jours.
Gerer un projet Open Source demande beaucoup de volonte.
Avatar de Sange844 Sange844 - Membre du Club https://www.developpez.com
le 16/11/2018 à 13:34
"des alternatives éprouvées telles que Rust, Swift, pouvant être considérées comme des langages « memory safe » semblent à la traine"

Je trouve que Rust se développe très bien : de plus en plus de cas usuels se retrouvent pris en charge par des libraries de plus en plus matures, je vois plus d'offres d'emploi passer, Rust est toujours le language le plus apprécié par ses adeptes selon StackOverflow, etc. Pour sûr l'écosystème pourrait connaitre une croissance plus rapide, mais c'est toujours le cas avec n'importe quelle technologie.

Je me demande quels arguments ont forgé cette opinion d'un lagnuage "à la traine".
Avatar de KsassPeuk KsassPeuk - Membre averti https://www.developpez.com
le 16/11/2018 à 13:41
Non mais c'est un mystère pour personne en fait. Tous les acteurs du domaine le savent depuis genre 30 ou 40 ans.

Cependant, il faut déjà bien comprendre qu'on n'utilise pas ces langages dans les domaines en question parce que c'est les meilleurs, mais parce que ça a été les seuls capables d'amener au niveau de performances requis pendant des dizaines d'années (et on choisit très souvent de privilégier les performances à la fiabilité quand on veut vendre à un très grand nombre). Donc à la question "pourquoi Swift ou Rust mettent 'du temps' à décoller ?". Ils mettent pas du temps, ils viennent d'apparaître, on va pas transformer des milliards de lignes de code du C à C++ en deux coups de cuillère à pot, et c'est pas le tout que le langage existe, il faut former les gens aussi, ça va pas se faire en deux jours.

Ensuite, il vient la question d'assurer la fiabilité. Il est clairement possible d'assurer des niveaux de fiabilité très élevés sur des langages comme C et C++. C'est fait depuis des années dans le ferroviaire, l'aviation, l'armement, seulement il faut y mettre les moyens. Et rien ne dit qu'on atteindrait le même niveau facilement avec d'autres langages comme Rust. Je crois personnellement que c'est possible, effectivement, mais l'outillage d'un langage comme Rust est encore, dû à sa jeunesse, encore très loin des outils qui sont aujourd'hui disponibles pour C ou C++. Et vous faites confiance au compilateur Rust ? Moi pas. Mais pour C, il existe des choses du niveau de CompCert (et s'il y a des travaux préliminaires pour Rust, on en est encore très loin). Alors on prend quoi ? Un langage pour lequel j'ai pas d'outil avancé de vérification et un compilateur qui n'est pas sûr ? Ou un langage pour lequel j'ai des outils ultra avancé de vérification et un compilateur certifié par preuve formelle ?

Finalement, il faut pas oublier qu'il reste encore une option qui n'exclut pas des langages comme C. La génération de code correct par construction. Comme on peut le faire à partir de Lustre ou de langages comme Low-Star (basé sur F-Star). Je veux faire une lib de sécu, je fais quoi ? Je code en Rust sans aucune garantie que mon code est conforme à la spécification ? Où je l'écris en F-Star, je le prouve, je génère le C de manière certifiée et je le compile avec CompCert ? (Coucou HACL* !).
Avatar de e101mk2 e101mk2 - Membre confirmé https://www.developpez.com
le 16/11/2018 à 14:04
Si des langages interprété sont exempte de ces problèmes, c'est qu'il est possible d'en faire autant en C ou C++ moyennement quelques lignes de codes/Directive préprocesseur.

Si le C/C++ à de bonne performance, c'est parce que justement il ne vérifie pas si on va faire un buffer overflow, « use after free », ou la vérification de type. C'est au développeur de s'assurer qu'il y'en as pas, en rajoutant des instructions de vérification.

Utiliser Python ou Java comme indiquée dans l'article est une solution, qui préfèrent la sécurité au performance.

Pour ce qui est de Rust et Swift, je ne les connait que de noms. Mais si ils ont trouvée des solutions, c'est soit, la syntaxe du langage qui empêche ces problèmes, soit des instructions processeur supplémentaire qui alourdissement l’exécution.
Avatar de KsassPeuk KsassPeuk - Membre averti https://www.developpez.com
le 16/11/2018 à 14:16
Citation Envoyé par e101mk2 Voir le message
Pour ce qui est de Rust et Swift, je ne les connait que de noms. Mais si ils ont trouvée des solutions, c'est soit, la syntaxe du langage qui empêche ces problèmes, soit des instructions processeur supplémentaire qui alourdissement l’exécution.
Ce n'est pas syntaxique, c'est sémantique. Rust repose sur des "linear-types" (pour ceux qui veulent jeter un oeil à la partie théorique), pour garantir l'absence de runtime errors liées à la mémoire statiquement et permettre la libération automatique de mémoire sans GC, et de manière déterministe (et par comptage de référence si le développeur ne sait pas comment faire mieux). Il s'ajoute aussi à cela du contrôle automatique de bornes qui (en fait) dans la très vaste majorité des cas (pour ne pas dire tout le temps) peut être automatiquement virée par le compilateur. En gros, si un contrôle est laissé par le compilateur, ça veut très probablement dire que le développeur est de toute façon en train de faire une connerie.

Et c'est pour ça que ce serait intéressant d'avoir des analyseurs sémantiques encore plus puissants (du niveau de ce qu'on sait faire en C) pour Rust (interprétation abstraite par exemple), ça pourrait permettre de transmettre des hypothèses supplémentaires au compilateur pour virer encore plus de choses.
Avatar de onilink_ onilink_ - Membre éprouvé https://www.developpez.com
le 16/11/2018 à 14:26
Citation Envoyé par KsassPeuk Voir le message
Finalement, il faut pas oublier qu'il reste encore une option qui n'exclut pas des langages comme C. La génération de code correct par construction. Comme on peut le faire à partir de Lustre ou de langages comme Low-Star (basé sur F-Star). Je veux faire une lib de sécu, je fais quoi ? Je code en Rust sans aucune garantie que mon code est conforme à la spécification ? Où je l'écris en F-Star, je le prouve, je génère le C de manière certifiée et je le compile avec CompCert ? (Coucou HACL* !).
Intéressant, je ne connaissais pas F-Star. Reste a voir si ce n'est pas trop compliqué à utiliser.

Citation Envoyé par e101mk2 Voir le message
Si le C/C++ à de bonne performance, c'est parce que justement il ne vérifie pas si on va faire un buffer overflow, « use after free », ou la vérification de type. C'est au développeur de s'assurer qu'il y'en as pas, en rajoutant des instructions de vérification.
[...]
Pour ce qui est de Rust et Swift, je ne les connait que de noms. Mais si ils ont trouvée des solutions, c'est soit, la syntaxe du langage qui empêche ces problèmes, soit des instructions processeur supplémentaire qui alourdissement l’exécution.
En fait Rust est juste beaucoup mieux pensé que le C++ ou d'autres langages, pour la sécurité. Pour le comprendre il faut au moins lire un peu de documentation.
Le langage est beaucoup plus strict sur plein de niveaux, et il n'a effectivement pas peur de faire quelques concessions sur les performances (comme le bound checking), mais tout cela de manière intelligente (ainsi, si on code correctement il n'y aura que très peu de perte de performances, car le compilateur pourra supprimer pas mal de "checks").

Par contre, je le trouve difficile à apprendre. Certains disent que le C++ est difficile... il n'ont pas vu Rust
C'est un peu comme quand on tente d'apprendre Vulkan et que les premières lignes indiquent "contrairement a une API haut niveau comme OpenGL [...]"
Avatar de KsassPeuk KsassPeuk - Membre averti https://www.developpez.com
le 16/11/2018 à 14:35
Citation Envoyé par onilink_ Voir le message
Intéressant, je ne connaissais pas F-Star. Reste a voir si ce n'est pas trop compliqué à utiliser.
Le but en soi avec ce langage c'est de produire des programmes qui pourraient (sur le papier, pas encore officiellement je pense) être certifié CC-EAL7. Donc, il faut pas d'attendre à un truc facile. Mais c'est plutôt à utiliser pour des composants ultra-critiques (et c'est, au fond, pas plus dur qu'atteindre le même niveau de fiabilité sur du C : ce serait même plus facile, c'est fait pour). Ça reste un langage avec des types dépendants et de la preuve formelle, donc des coûts de développement élevés et des utilisateurs qui ont besoin d'un bon niveau en maths.
Avatar de Pyramidev Pyramidev - Membre expert https://www.developpez.com
le 16/11/2018 à 14:49
Citation Envoyé par onilink_ Voir le message
Par contre, je le trouve difficile à apprendre. Certains disent que le C++ est difficile... il n'ont pas vu Rust
Je ne connais pas encore Rust aussi bien que C++ mais, après avoir lu en entier la 2e édition de The Rust Programming Language (qui est une introduction au langage), je n'ai pas eu l'impression qu'il était plus compliqué que C++. Au contraire, je pense encore que C++ est probablement plus compliqué que Rust.

Cependant, j'avais déjà étudié Haskell avant de me pencher sur Rust. Du coup, dans Rust, je n'ai pas été déstabilisé par les traits (qui sont similaires aux type classes de Haskell) et j'étais déjà habitué à certaines fonctionnalités comme le pattern matching.

Rust est plus éloigné de Java que ne l'est C++, surtout si on privilégie la programmation orientée objet quand on programme en C++.
Avatar de onilink_ onilink_ - Membre éprouvé https://www.developpez.com
le 16/11/2018 à 15:12
@KsassPeuk
Je vois, merci pour les infos

Citation Envoyé par Pyramidev Voir le message
Je ne connais pas encore Rust aussi bien que C++ mais, après avoir lu en entier la 2e édition de The Rust Programming Language (qui est une introduction au langage), je n'ai pas eu l'impression qu'il était plus compliqué que C++. Au contraire, je pense encore que C++ est probablement plus compliqué que Rust.

Cependant, j'avais déjà étudié Haskell avant de me pencher sur Rust. Du coup, dans Rust, je n'ai pas été déstabilisé par les traits (qui sont similaires aux type classes de Haskell) et j'étais déjà habitué à certaines fonctionnalités comme le pattern matching.

Rust est plus éloigné de Java que ne l'est C++, surtout si on privilégie la programmation orientée objet quand on programme en C++.
Peut être que je vois les choses plus difficiles car j'ai juste lu la documentation, et que je n'ai jamais fait beaucoup de fonctionnel, mais je trouve C++ assez simple s'il est appris correctement (donc déjà, pas en commençant par C, pas en apprenant C++98 ...), surtout si on se limite à une utilisation standard sans trop approfondir.
C'est sûrement aussi le cas de Rust.

Mais Rust il y a beaucoup de règles à connaître, et pas forcement des choses intuitives, comme les références et le borrowing.
On ne sait pas trop par ou commencer quand on veut écrire son "premier vrai programme".

Après c'est peut être tout simplement mon gros bagage C++ qui fait que je vais forcement toujours vouloir "penser C++"/impératif dans un autre langage (ce qui est plus un frein qu'autre chose pour apprendre un langage tel que Rust).

En tout cas, même si je ne compte pas changer de langage de ci tôt, Rust donne vraiment envie.
Il a l'air d'être pensé de manière très intelligente, et même s'il semble assez difficile d'utilisation (au premier abord), ça fait du bien de voir des langages "modernes" qui privilégient la sécurité et les performances plutôt que le côté facile d'utilisation (qui présuppose toujours que les dev sont des idiots).
Avatar de SimonDecoline SimonDecoline - Membre chevronné https://www.developpez.com
le 16/11/2018 à 15:17
Citation Envoyé par Pyramidev Voir le message
Rust est plus éloigné de Java que ne l'est C++, surtout si on privilégie la programmation orientée objet quand on programme en C++.
Oui. Rust est plus un langage fonctionnel qu'objet donc forcément.

Concernant l'article, s'il a raison sur le fond, il est quand même un peu biaisé. Déjà dans les années 90, on ne risquait pas de coder en Rust ou en Swift... Ensuite C++ a beaucoup évolué donc il ne faudrait pas non plus comparer le Rust de 2018 avec le C++ de 98. Enfin Rust est poussé par Mozilla, donc l'avis d'un ingé de Mozilla n'est pas forcément des plus objectifs.
Contacter le responsable de la rubrique Accueil