Speaker #0Bonjour mamie, bonjour et bienvenue dans la cybersécurité expliquée à ma grand-mère, le podcast pour expliquer la cybersécurité à des gens qui ne comprennent rien. Le Tractatus Logico-Philosophicus, souvent abrégé en Tractatus, est un livre de philosophie écrit par Ludwig Wittgenstein. Dans ce livre, Wittgenstein essaie de répondre à des questions fondamentales sur le langage, la pensée et la réalité. Il est connu pour son style concis et ses propositions numérotées qui construisent une sorte d'argument logique en plusieurs étapes. Ludwig Wittgenstein était un philosophe autrichien né le 26 avril 1989 à Vienne et décédé le 29 avril 1951 à Cambridge. Il est considéré comme l'un des philosophes les plus influents du XXe siècle, notamment dans le domaine de la logique, de la philosophie du langage et de la philosophie de l'esprit. C'est un personnage haut en couleur à propos duquel je raconterai quelques anecdotes à la fin de cet épisode. L'une des idées principales du Tractatus est que le langage et la réalité ont une structure similaire. Wittgenstein propose que le langage décrit le monde en utilisant des images ou des représentations des faits. Selon lui, une phrase est une image de la réalité. Et pour qu'une phrase ait un sens, elle doit correspondre à un fait possible dans le monde. Wittgenstein fait également une distinction importante entre ce qui peut être dit clairement et ce qui doit être passé sous silence. Il pense que les questions les plus profondes de la vie, comme celles concernant l'éthique, la religion ou le sens de la vie, ne peuvent pas être exprimés dans le langage ordinaire. En d'autres termes, ce qui est vraiment important dépasse les limites du langage et ne peut être montré que par le silence ou des actes. Le tractacus se termine par la fameuse proposition Ce dont on ne peut parler, il faut le taire Cela signifie que lorsqu'il s'agit de choses qui ne peuvent pas être exprimées clairement par le langage, il vaut mieux ne rien dire du tout. Mais que faut-il comprendre par ce qui peut être exprimé clairement Le cœur de la philosophie de Wittgenstein consiste à nous montrer les limites du langage, et non à nous dire les limites du langage. En fait, cette idée est assez simple à comprendre, car pour montrer les limites du langage, il faudrait que le langage puisse décrire cette limite. Et donc, à cet instant précis, cette limite se déplace. Dire la limite du langage est donc un objectif inatteignable. Mais pourquoi ce n'est pas possible ? Pour cela, on va prendre un exemple concret. On peut dire qu'une tomate est rouge ou qu'il y a un moment où la tomate est rouge. Mais il est impossible de répondre verbalement à la question qu'est-ce que le rouge ? Le rouge, comme d'autres concepts, ne peut pas être défini par des mots. Au lieu de cela, on peut seulement montrer quelque chose de rouge et dire le rouge c'est ça On peut identifier ce qui est rouge ou non, car cela relève du fait observable et vérifiable dans le monde. Cependant, expliquer ce qu'est le rouge de manière abstraite et verbale est impossible. on ne peut que le montrer et pas le dire. Le rouge n'a pas d'essence propre en soi qui permettrait de le définir de manière autonome. Il n'a de sens qu'au sein d'un fait concret et observable. Cela soulève un problème fondamental. Il existe une énorme différence entre dire et montrer. Selon Wittgenstein, Le langage est des limites, et certaines choses ne peuvent pas être exprimées que par le biais de démonstrations directes, plutôt que par des descriptions verbales. A noter que c'est exactement le problème que l'on retrouve avec l'IA générative. L'IA peut vous dire qu'une tomate est rouge, mais ne sait pas ce qu'est le rouge. Contrairement à un humain, l'IA n'a jamais expérimenté ce qu'est le rouge. Il y a donc un problème de correspondance entre le langage et la réalité. La réalité est bien plus vaste que ce que le langage est capable de décrire. C'est un peu comme verser un litre d'eau dans une tasse à café, ça va forcément déborder.
Speaker #0Et bien c'est précisément ce qui se passe avec le langage. Il existe une limite structurelle qui l'empêche de pouvoir tout dire de façon factuelle. Certains d'entre vous verront peut-être en filigrane le théorème de l'incomplétude de Kurtz-Güdel. Mais en quoi le tractacus peut nous aider à comprendre la cybersécurité ? Et bien c'est précisément l'une des questions fondamentales de la cybersécurité. Est-il possible d'imaginer un langage de programmation parfaitement sûr ? C'est-à-dire un langage de programmation qui ne donnerait aucune possibilité à un attaquant d'exploiter une faille. Avant de plonger dans le détail de cette question, il faut dans un premier temps définir ce qu'est un langage de programmation et ce qu'est une faille de sécurité. Un langage de programmation est un outil qui permet au développeur de donner des instructions à un ordinateur pour qu'il exécute des tâches spécifiques. C'est un ensemble de règles et de syntaxes que le programmeur utilise pour écrire du code. Ce code est ensuite traduit en instructions compréhensibles par la machine, qui les exécute pour effectuer des opérations diverses, comme des calculs, la gestion des données ou l'affichage d'informations sur l'écran. Un langage de programmation sert d'intermédiaire entre l'humain et l'ordinateur, permettant de transformer des idées et des solutions en actions concrètes exécutées par la machine. Dans le contexte d'un langage de programmation, une faille de sécurité est une vulnérabilité ou une faiblesse, dont le code écrit avec ce langage qui peut être exploité par des attaquants pour compromettre la sécurité d'une application d'un système. Ces failles peuvent survenir en raison d'un bug, d'erreurs de logique ou de mauvaises pratiques de programmation. Mais est-ce que les langages de programmation peuvent se prémunir de ce genre de problème ? Avant de répondre à cette question, il faut expliquer quelles sont les différentes catégories de langages de programmation. Car en réalité, il en existe une myriade. Et c'est assez logique puisqu'il existe des contextes et techniques différents. En informatique, on parle de paradigme. Un paradigme est un modèle ou une approche de programmation qui guide la manière dont les programmes sont structurés et les solutions sont formulées. Un paradigme de programmation fournit un cadre conceptuel et un ensemble de principes qui influencent la manière dont les développeurs pensent et organisent leur code. Par exemple, le paradigme impératif organise le code comme une suite séquentielle d'instructions. C'est le cas du langage C. Le langage C est un langage de programmation créé dans les années 1970 par Denis Ricci au Bell Labs. Conçu pour écrire le système d'acclatation Unix, C est reconnu par sa performance, sa flexibilité et son efficacité. Il offre une grande proximité avec le matériel informatique, permettant une gestion fine de la mémoire et des ressources. C'est aussi l'un des langages de programmation qui offre le meilleur rendement énergétique. Le langage C est largement utilisé dans le développement des systèmes d'exploitation comme Unix ou Linux, les logiciels embarqués, les applications de performance critique, ainsi que dans l'enseignement de la programmation et des concepts de bas niveau. Le langage C est apprécié pour sa puissance, sa flexibilité et son efficacité, mais vous avez peut-être aussi entendu parler de langage orienté objet. Dans ce cas, on organise le code autour de classes. C'est le cas de Java, C++ et de Python. Un autre paradigme est le paradigme déclaratif, comme le SQL ou le HTML. Dans ce cas, l'utilisateur ne va pas décrire à la machine les instructions à exécuter, mais va décrire le résultat souhaité. C'est le cas par exemple d'une requête SQL. C'est un langage très populaire pour interroger une baisse de données. Dans ce cas, l'utilisateur va décrire sa recherche, mais sans pour autant donner les détails techniques, sur où sont stockées les données dans le système par exemple. Le but de cet épisode n'est pas de détailler chacun de ces paradigmes, Il y en aurait beaucoup trop et nous avons ici parlé que de quelques exemples. Mais il faut simplement comprendre qu'il existe plusieurs moyens de programmer et ou d'interagir avec un système d'information. Il faut comprendre que chaque paradigme offre une manière différente de penser les problèmes de programmation et de structurer les solutions. Et certains langages de programmation supportent plusieurs paradigmes, permettant aux développeurs de choisir celui qui convient le mieux à une tâche spécifique. Et en plus des différents paradigmes de programmation, il existe aussi plusieurs méthodes d'exécution. Il en existe principalement trois. La première est la compilation, qu'il faut comprendre comme une méthode de traduction entre le langage utilisé par le programmeur, qui est compréhensible par l'humain, et le langage de la machine. Il est techniquement possible de programmer directement dans le langage de la machine. Il est assez difficile dans ce cas de manipuler des concepts complexes, comme par exemple dans le cadre de la programmation orientée objet. En revanche, passer de l'un à l'autre est un processus automatisable. C'est exactement le rôle du compilateur. Partir d'un langage de programmation structuré qui utilise des paradigmes plus ou moins complexes et de traduire ceux-ci en code exécutable par la machine. Cela permet aussi d'organiser et d'utiliser des composants extérieurs, ce qui permet la réutilisation du code et augmenter la productivité. Cela fonctionne généralement très bien, mais avec une petite contrainte. C'est que le compilateur doit prendre en compte la machine sur laquelle va s'exécuter le code, le type de système d'exploitation, le CPU, etc. Ceci implique des contraintes techniques, car il peut exister des petites différences entre les systèmes, par exemple la représentation d'un nombre. La taille de la représentation de ce nombre peut changer d'un système à un autre. Il y a aussi le système d'exploitation lui-même. Vous avez tous déjà remarqué qu'il n'est pas possible d'exécuter un programme fait pour tourner sur une machine Windows, sur un Mac ou sur Linux. Même si le programme est le même, c'est-à-dire que le code source est le même, vous devrez recompiler le code pour qu'il puisse s'exécuter sur un autre type de système. Pour pallier à ce problème, certains langages produisent un code intermédiaire qui s'exécutera dans un programme spécifiquement écrit pour la machine cible. C'est le cas de Java. Le compilateur Java va produire ce qu'on appelle du bytecode, qui ne sera pas exécuté directement sur la machine, mais dans un programme appelé Java Virtual Machine, qui lui est spécifique à la machine. C'est un peu comme si le bytecode pouvait s'exécuter de manière universelle. Le code est identique quelle que soit la plateforme. Seule la Java Virtual Machine est différente. Mais dans ce contexte, Il existe encore une étape de compilation qui, rappelons-le, a pour but de traduire du code d'un langage à un autre. Eh bien il existe un troisième cas de figure qui s'affranchit de cette étape. C'est le cas du code interprété. C'est le cas du langage Python par exemple. En fait, l'interpréteur a pour but d'exécuter directement les instructions données par le programmeur, sans passer par une phase de compilation. Interprète ! Interprète ! Interprète ! Interprète ! Oui, hier ! Cette notion existe depuis bien longtemps car dans les systèmes Unix par exemple, il existe des interpréteurs qui permettent d'exécuter des scripts. C'est la raison pour laquelle on parle très souvent de scripts dans ce cas et non de programmes. Les interpréteurs permettent de s'affranchir de la phase de compilation et assurent la portabilité du code. C'est-à-dire que le code puisse s'exécuter de manière identique sur des environnements différents. Le revers de la médaille est que les interpréteurs sont souvent plus lents que les langages compilés. Mais quel est donc le problème en matière de cybersécurité ? En quoi les différents paradigmes et modes d'exécution peuvent poser des problèmes ? Ou plutôt, comment peuvent-ils nous aider à résoudre le problème de la cybersécurité ? En fait, le problème est assez vaste et avant toute chose, il faut comprendre qu'à l'instar de Wittgenstein et le Tractatus Logico-Philosophicus, il n'est pas possible de demander à un langage de programmation de définir ce qui est malveillant et de ce qui ne l'est pas. Toutefois, le sujet de la cybersécurité... est telle qu'on ne peut pas simplement se contenter de dire on ne peut pas, donc on ne fera rien C'est la raison pour laquelle plusieurs initiatives ont été lancées pour mieux contrôler et ou minimiser les risques. Mais au fait, quels sont les problèmes que l'on peut rencontrer ? La liste est malheureusement assez longue et je ne couvrirai ici que quelques cas. Commençons par les accès à la mémoire. Beaucoup de failles sont dues à des accès plus ou moins illicites à la mémoire, ce qui peut dans certains cas permettre la manipulation de certaines données liées aux... au programme ou à son exécution directement, par exemple détourner l'exécution du programme. Concrètement, c'est manipuler une entrée du programme en jouant sur la longueur et la nature des données fournies pour obtenir cet effet. Un autre problème peut être une mauvaise gestion des erreurs. Imaginez que votre système gère mal les erreurs et qu'il lui faille 10 secondes à chaque fois qu'il doit en traiter une. Un acteur malveillant pourrait envoyer de manière répétitive des données non conformes afin de faire s'effondrer votre système. Il y a aussi ce qu'on appelle les rest conditions C'est un problème qui survient dans les systèmes concurrents lorsque le comportement du logiciel dépend de l'ordre ou du timing des événements non contrôlés. Cela peut se produire lorsque deux ou plusieurs processus accèdent et modifient des ressources partagées de manière concurrente, sans une synchronisation appropriée. L'ordre dans lequel les processus accèdent à la ressource n'est pas déterminé à l'avance et peut varier d'une exécution à une autre, entraînant des résultats imprévisibles et incorrects. Si les excels à ressources partagées ne sont pas correctement synchronisés, par exemple à l'aide de mécanismes de verrouillage, il peut en résulter des comportements inattendus et des corruptions de données. Il existe une liste très longue de points d'attention dont le spectre s'étale des problèmes de qualité de code à des problèmes purement de sécurité. Les langages les plus anciens, comme C par exemple, n'ont pas de contraintes qui permettent ou pas de faire du code sûr et de bonne qualité. Il y a eu des tentatives comme par exemple ADA. ADA est un langage de programmation structuré. Il a été développé dans les années 80 par une équipe sous contrat avec le département de la Défense des États-Unis. ADA est utilisé dans des domaines critiques comme l'aérospatiale, la défense, les systèmes embarqués et autres applications où la sécurité et la fiabilité sont primordiales. Il y a aussi Eiffel. Eiffel est un langage de programmation orienté objet conçu par Bertrand Meyer dans les années 80. Il met fortement l'accent sur la robustesse, la réutilisabilité et la maintenabilité du code. Eiffel est particulièrement utilisé dans des projets où la qualité et la fiabilité du logiciel sont cruciales, comme dans les domaines de l'ingénierie logicielle et des systèmes critiques. Il y a plus récemment Rust. Rust est un langage de programmation moderne développé par Mozilla Research qui met l'accent sur la sécurité, la performance et la concurrence. Voici quelques-unes de ses caractéristiques principales. Rust utilise un système de priorité unique pour gérer la mémoire. Rust va améliorer la sécurité de la mémoire, mais va aussi gérer la concurrence entre les processus. Rust est utilisé dans divers domaines, notamment les systèmes d'exploitation, les navigateurs web, les moteurs de jeu et les applications en temps réel, où la performance et la sécurité sont cruciales. Mais à bien y réfléchir, même si les langages embarquent dans leurs structures les principes qui permettent de limiter les risques en matière de cybersécurité, aucun ne peut se prévaloir d'être totalement sécurisé. C'est pas faux. Tout simplement car la notion de malveillance n'est pas déterministe. C'est ce que Wittgenstein indique à la fin du Tractatus. Ce dont on ne peut pas parler, il faut le taire. Il ne faut pas comprendre ici qu'il faut cacher des choses, mais simplement que certaines choses ne peuvent pas être définies dans le langage. C'est exactement le même problème qu'avec la couleur rouge dont on a parlé au début de cet épisode. Vous ne pouvez pas dire ce qui est malveillant, mais vous pouvez au mieux le montrer. Bien que Ludwig Wittgenstein fût très probablement un génie, il n'en était pas moins quelqu'un d'assez insupportable par son comportement. Il était excentrique à bien des égards. Il aimait beaucoup les westerns, par exemple. Il disait que ces films agissaient sur son esprit comme une douche intellectuelle, une sorte de thalassothérapie des neurones. Alors la prochaine fois que vous êtes dans votre canapé et regardez une americanerie sur Netflix, et que quelqu'un vous en fait le reproche, dites-lui simplement que vous appliquez la même méthode que Ludwig Wittgenstein pour comprendre le monde qui vous entoure et découvrir les secrets de l'univers. Encore merci d'avoir écouté cet épisode de la cybersécurité expliquée à ma grand-mère. N'hésitez pas à le liker et à le partager avec d'autres et en parler autour de vous. Si vous êtes sur Spotify, vous pouvez aussi donner votre avis et proposer des sujets qui vous semblent pertinents. Mais surtout n'oubliez pas, pour certains, la cybersécurité est un enjeu de vie ou de mort, c'est bien plus sérieux que ça.