Python 2 vs 3 : différences en matière de sécurité
10 octobre 2017
0 minutes de lecturePython 3 et Python 2 présentent diverses différences fonctionnelles. En soi, elles ne sont pas nécessairement meilleures ou moins bonnes (même si Python 3 devrait, en théorie, apporter des améliorations), mais tout changement peut introduire des risques. Cet article met en évidence et explique quelques différences entre les versions qui ont des implications en matière de sécurité. Gardez-les à l’esprit lorsque vous migrez des applications Python d’une version à l’autre.
État actuel des versions 2 et 3 de Python
Python 3 est sorti pour la première fois en 2008. L’un des problèmes qui a freiné son adoption est son absence de compatibilité ascendante. De nombreux développeurs ont continué à utiliser les versions précédentes, principalement la version 2, pour éviter de devoir refactoriser leurs applications. C’est pourquoi la prise en charge de Python 2 s’est poursuivie, et son développement est resté actif après la sortie de la version 3.0.
La version 2.7 est la dernière version de Python 2, toujours utilisée activement par de nombreux développeurs. Python 2.7 est sortie en 2010, et la Python Foundation a annoncé sa fin de vie pour 2020.
Python 3.6 est la version actuelle de Python. Elle est sortie pour la première fois en 2016. Python 3 est considéré comme l’avenir du langage de programmation Python.
Python 2 vs 3 : principales différences entre les versions et implications en matière de sécurité
Les principales différences entre Python 2 et Python 3 sont présentées ci-dessous. Aucun de ces changements n’est axé sur la sécurité, mais chacun a des implications en la matière dont vous devez tenir compte.
Chaînage des exceptions
Python 3 a introduit le chaînage des exceptions par défaut. Dans Python 2, lorsqu’une exception se produit dans une bibliothèque appelée par le programme Python principal, les détails de cette exception ne sont généralement pas affichés dans la trace d’exécution. Dans Python 3, la trace d’exécution affiche tous les détails des exceptions, qu’elles soient générées par le programme principal ou par les bibliothèques appelées.
Toute information interne à votre code ou à son comportement à l’exécution, lorsqu’elle est exposée à l’utilisateur, représente un risque pour la sécurité. Le chaînage des exceptions, bien qu’utile pour le débogage, révèle beaucoup plus d’informations à un attaquant potentiel, qui peut générer une trace d’exécution pour voir comment le logiciel réagit aux erreurs.
Vous pouvez désactiver le chaînage des exceptions dans Python 3 si vous estimez que les risques l’emportent sur les avantages.
Méthode de saisie
Python 2 propose la fonction eval(), réputée non sécurisée, car elle évalue le texte transmis en paramètre et accepte un deuxième argument facultatif indiquant les valeurs globales à utiliser pendant l’évaluation. Dans Python 2, la fonction input() fonctionne de manière similaire à eval() et présente les mêmes risques.
Les attaquants peuvent exploiter ces fonctions en intégrant des noms de variables dans leur saisie, et ainsi obtenir leur valeur, qui peut contenir des informations sensibles.
Par conséquent, une bonne pratique de sécurité dans Python 2 consiste à ne pas utiliser eval() ni input(), mais plutôt raw_input(), qui n’évalue pas les variables présentes dans le texte saisi.
Qu’est-ce qui a changé dans Python 3 ? raw_input() a été supprimée et ses fonctionnalités ont été transférées à une nouvelle fonction appelée input(). La nouvelle fonction input() fonctionne comme le faisait raw_input() dans Python 2 : son utilisation est donc désormais sans risque.
En revanche, si vous exécutez un programme Python 3 dans Python 2, toutes les saisies peuvent être exploitées.
En résumé :
Fonction | Python 2.x | Python 3.x |
|---|---|---|
| Évalue les variables : non sécurisé | Évalue les variables : non sécurisé |
| Évalue les variables : non sécurisé | N’évalue pas les variables, comme |
| N’évalue pas les variables | Supprimée |
Division entière
Python 2 gère la division entière d’une manière différente et moins intuitive que Python 3. Dans Python 2, la division de deux entiers renvoie toujours une valeur entière, à savoir le plus grand entier inférieur ou égal au résultat. Cette méthode de division est appelée division plancher :
La division entière est plus intuitive dans Python 3, car le langage n’applique pas automatiquement la division plancher lorsqu’il divise des entiers :
Cette manière plus intuitive de gérer la division entière n’est pas rétrocompatible avec Python 2. Il est donc particulièrement dangereux d’exécuter du code Python 3 dans Python 2, car le comportement de la division entière ne déclenche pas d’erreur de syntaxe.
Cela pose également un problème potentiel de sécurité. Prenons l’exemple d’une application qui demande un code PIN numérique, puis divise le code PIN enregistré dans le système par celui saisi par l’utilisateur pour indiquer si la saisie est correcte. Dans Python 3, cela fonctionnerait bien, mais l’exécution de la même application dans Python 2 laisserait passer de nombreuses tentatives erronées.
Cet exemple n’est pas réaliste, mais il montre que le fait de compter sur la division entière pour arrondir les nombres (dans Python 2) ou ne pas les arrondir (dans Python 3) est susceptible de modifier le comportement du programme. Les attaquants pourraient exploiter cette différence pour contourner des mécanismes de protection.
Prise en charge d’Unicode
Les deux versions de Python gèrent différemment les chaînes (séquences de caractères).
Python 2 utilise par défaut la norme d’encodage ASCII, qui ne permet de représenter que 256 caractères. La capacité de Python à encoder des caractères, en particulier ceux qui ne sont pas standard, s’en trouve limitée.
L’utilisation d’Unicode dans Python 2 nécessite une syntaxe supplémentaire. Par exemple, avec print, vous devez envelopper le texte saisi dans la fonction unicode() pour gérer les caractères spéciaux.
La norme Unicode est beaucoup plus polyvalente : elle prend en charge plus de 128 000 caractères. Dans Python 3, Unicode est utilisé par défaut. Aucune syntaxe supplémentaire n’est nécessaire pour définir des valeurs Unicode : elles s’affichent automatiquement sous forme de chaînes utf-8.
Bien que puissante, cette modification peut entraîner un risque important d’hameçonnage. Par exemple, il est possible de diriger les utilisateurs vers une URL dont le nom de domaine contient des caractères remplacés par des lettres d’alphabets non latins. Un article de Xudong Zheng montre ce que les utilisateurs pourraient voir :

À première vue, l’URL semble correcte, mais les apparences sont trompeuses. L’URL de l’exemple présenté par Xudong ressemble à « apple.com », mais il s’agit en réalité de « xn-pple-43d.com ». En utilisant, par exemple, le « a » cyrillique (U+0430) à la place du « a » ASCII (U+0061), vous pouvez amener certains navigateurs à masquer involontairement le véritable domaine. Le site semble donc sécurisé, alors qu’en réalité, l’utilisateur est dirigé vers un autre domaine qui peut présenter des risques. C’est ce qu’on appelle une attaque par homographe.
Cette attaque peut concerner Python si elle cible un chemin de répertoire analysé par l’application Python ou un paramètre de requête. Par exemple, si l’application expose un chemin http://domain.com/Acme, un attaquant peut remplacer les lettres « Acme » par des caractères d’autres alphabets et diriger les utilisateurs vers un autre compte.
Lors de la migration d’une application de Python 2 à Python 3, vous devez tenir compte du fait que toutes les saisies seront converties en Unicode, ce qui pourrait permettre à des attaquants de piéger vos utilisateurs.
Vulnérabilités et correctifs de sécurité dans Python 3.6 et 2.7
Des vulnérabilités graves ont été découvertes dans d’anciennes versions de Python 2 et 3. Il est recommandé de les éviter (consultez CVE Details pour connaître les versions de Python concernées).
Cependant, la plupart des vulnérabilités des dernières versions, 2.7.x et 3.6.x, ont été corrigées. Python 2 et 3 font actuellement l’objet d’une maintenance active et, tant que vous installez la dernière version, vous devriez être protégé contre les principales vulnérabilités. Cela dit, il est probable que la Python Software Foundation accorde moins d’attention à Python 2 et que les mises à jour de sécurité soient moins fréquentes que pour Python 3.
Plus important encore, Python 2 arrivera en fin de vie en 2020. À partir de cette date, le langage ne sera plus maintenu et aucun correctif de sécurité ne sera disponible. Si vous continuez à utiliser Python 2, gardez à l’esprit qu’à partir de 2020, le langage ne sera plus sécurisé et commencez à planifier votre migration vers Python 3.
Graphes de dépendances différents, bibliothèques vulnérables différentes
L’absence de compatibilité ascendante de Python 3 a obligé pip, le principal gestionnaire de paquets de Python, à créer également deux branches, 2.x et 3.x, et à répertorier les paquets compatibles avec chaque version. Par conséquent, la migration d’une application nécessite souvent de modifier les bibliothèques qu’elle utilise, ou au moins leurs versions.
Comme tout logiciel, ces bibliothèques ne sont pas parfaites et des vulnérabilités sont découvertes de temps à autre. Une fois celles-ci divulguées, les propriétaires d’applications doivent se dépêcher de les corriger avant que les attaquants ne les exploitent. Equifax, par exemple, a largement perdu cette course.
Les correctifs des vulnérabilités ne sont souvent pas portés sur toutes les versions possibles. Il est donc essentiel d’inclure des tests portant sur les vulnérabilités connues dans votre processus de migration. Si, pour une raison ou une autre, vous conservez Python 2, le risque de vulnérabilités est encore plus élevé, car les bibliothèques abandonnent de plus en plus la prise en charge de l’ancienne version, y compris pour les correctifs de sécurité.
Quelle que soit la version de Python que vous choisissez, vous pouvez tester votre application pour détecter les vulnérabilités connues avec Snyk, grâce à notre intégration GitHub, notre interface de ligne de commande ou d’autres plateformes intégrées.


