Hachage sécurisé avec Python Hashlib



Ce tutoriel vous initiera à la création de condensats sécurisés grâce aux fonctions intégrées du module hashlib de Python.

Comprendre l’importance du hachage et comment calculer par programmation des condensats sécurisés peut s’avérer précieux, même si vous ne travaillez pas spécifiquement dans le domaine de la sécurité des applications. Mais quel est l’intérêt ?

En effet, lors de vos projets Python, vous serez probablement confronté à des situations où la protection du stockage de mots de passe ou d’autres informations confidentielles dans des bases de données ou des fichiers de code source sera une préoccupation majeure. Dans de tels cas, il est plus sûr d’appliquer un algorithme de hachage aux informations sensibles et de sauvegarder le condensat plutôt que les données elles-mêmes.

Dans ce guide, nous allons définir ce qu’est le hachage et en quoi il se distingue du chiffrement. Nous examinerons également les caractéristiques des fonctions de hachage sécurisées. Ensuite, nous emploierons des algorithmes de hachage courants pour calculer le condensat d’un texte en clair en Python, en utilisant le module hashlib inclus par défaut.

Ceci étant dit, entrons dans le vif du sujet !

Qu’est-ce que le Hachage ?

Le hachage est le processus qui consiste à prendre une chaîne de caractères et à produire une sortie de longueur fixe, appelée condensat. Cela signifie que la longueur du condensat produit par un algorithme de hachage donné est constante, indépendamment de la longueur de l’entrée. En quoi cela diffère-t-il du chiffrement ?

Dans le chiffrement, le message ou texte brut est converti en une forme illisible à l’aide d’un algorithme de chiffrement. Il est ensuite possible de récupérer le message original en appliquant un algorithme de déchiffrement.

Cependant, le hachage fonctionne différemment. Nous venons de voir que le chiffrement est réversible, car on peut passer du texte chiffré au texte clair, et vice-versa.

À l’inverse du chiffrement, le hachage n’est pas un processus réversible. Cela signifie qu’il n’est pas possible de retrouver le message original à partir de son condensat.

Propriétés des Fonctions de Hachage

Voici les principales caractéristiques que les fonctions de hachage doivent respecter :

  • Déterminisme : une fonction de hachage est déterministe. Ainsi, pour un message donné m, le condensat de m sera toujours le même.
  • Résistance à la pré-image : nous avons déjà vu qu’il est impossible de retrouver le message original à partir de son condensat. La résistance à la pré-image implique qu’il est impossible de trouver un message m à partir de son condensat.
  • Résistance aux collisions : il doit être difficile (voire impossible du point de vue calcul) de trouver deux messages différents, m1 et m2, dont les condensats seraient identiques. Cette propriété est la résistance aux collisions.
  • Résistance à la seconde pré-image : étant donné un message m1 et son condensat correspondant m2, il est impossible de trouver un message différent m3 tel que hash(m1) = hash(m3).

Le Module hashlib de Python

Le module hashlib, inclus dans Python, propose des implémentations de plusieurs algorithmes de hachage et de résumé de message, notamment les algorithmes SHA et MD5.

Pour utiliser les constructeurs et les fonctions du module hashlib, importez-le dans votre environnement de travail, comme ceci :

import hashlib

Le module hashlib fournit les constantes algorithms_available et algorithms_guaranteed. La première renvoie l’ensemble des algorithmes disponibles, tandis que la seconde renvoie l’ensemble des algorithmes dont l’implémentation est garantie sur la plateforme en question.

algorithms_guaranteed est donc un sous-ensemble de algorithms_available.

Démarrez un interpréteur Python, importez hashlib et affichez le contenu des constantes algorithms_available et algorithms_guaranteed :

>>> hashlib.algorithms_available
# Résultat
{'md5', 'md5-sha1', 'sha3_256', 'shake_128', 'sha384', 'sha512_256', 'sha512', 'md4', 
'shake_256', 'whirlpool', 'sha1', 'sha3_512', 'sha3_384', 'sha256', 'ripemd160', 'mdc2', 
'sha512_224', 'blake2s', 'blake2b', 'sha3_224', 'sm3', 'sha224'}
>>> hashlib.algorithms_guaranteed
# Résultat
{'md5', 'shake_256', 'sha3_256', 'shake_128', 'blake2b', 'sha3_224', 'sha3_384', 
'sha384', 'sha256', 'sha1', 'sha3_512', 'sha512', 'blake2s', 'sha224'}

On voit bien que algorithms_guaranteed est un sous-ensemble de algorithms_available.

Comment Créer des Objets de Hachage en Python

Voyons comment créer des objets de hachage en Python. Nous allons calculer le condensat SHA256 d’une chaîne de caractères en utilisant les méthodes suivantes :

  • Le constructeur générique new()
  • Les constructeurs propres à chaque algorithme

Utilisation du constructeur new()

Commençons par initialiser la chaîne de caractères :

>>> message = "toptips.fr is awesome!"

Pour instancier l’objet de hachage, utilisons le constructeur new(), en y passant le nom de l’algorithme :

>>> sha256_hash = hashlib.new("SHA256")

Nous pouvons ensuite appeler la méthode update() sur l’objet de hachage, en y passant la chaîne de caractères :

>>> sha256_hash.update(message)

Si vous tentez cette opération, une erreur se produira, car les algorithmes de hachage ne fonctionnent qu’avec des chaînes d’octets.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Unicode-objects must be encoded before hashing

Pour obtenir la chaîne encodée, utilisez la méthode encode() sur la chaîne de caractères, puis passez-la comme argument à la méthode update(). Une fois cette étape effectuée, vous pouvez utiliser la méthode hexdigest() pour obtenir le condensat SHA256 correspondant.

sha256_hash.update(message.encode())
sha256_hash.hexdigest()
# Résultat: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Au lieu d’encoder la chaîne de caractères en utilisant la méthode encode(), vous pouvez aussi la définir comme une chaîne d’octets en préfixant la chaîne avec b comme ceci :

message = b"toptips.fr is awesome!"
sha256_hash.update(message)
sha256_hash.hexdigest()
# Résultat: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Le condensat obtenu est le même que le précédent, ce qui confirme le caractère déterministe des fonctions de hachage.

De plus, une petite modification de la chaîne de caractères doit entraîner un changement radical du condensat (c’est ce qu’on appelle l' »effet d’avalanche »).

Pour le vérifier, changeons le « a » de « awesome » en « A » et calculons le condensat :

message = "toptips.fr is Awesome!"
h1 = hashlib.new("SHA256")
h1.update(message.encode())
h1.hexdigest()
# Résultat: '3c67f334cc598912dc66464f77acb71d88cfd6c8cba8e64a7b749d093c1a53ab'

On observe que le condensat est complètement différent.

Utilisation du constructeur spécifique à l’algorithme

Dans l’exemple précédent, nous avons utilisé le constructeur générique new() et passé "SHA256" comme nom d’algorithme pour créer l’objet de hachage.

Il est également possible d’utiliser le constructeur sha256(), comme ceci :

sha256_hash = hashlib.sha256()
message= "toptips.fr is awesome!"
sha256_hash.update(message.encode())
sha256_hash.hexdigest()
# Résultat: 'b360c77de704ad8f02af963d7da9b3bb4e0da6b81fceb4c1b36723e9d6d9de3d'

Le condensat produit est identique à celui que nous avons obtenu précédemment pour la chaîne de caractères « toptips.fr is awesome! ».

Exploration des Attributs des Objets de Hachage

Les objets de hachage disposent de quelques attributs utiles :

  • L’attribut digest_size indique la taille du condensat en octets. Par exemple, l’algorithme SHA256 renvoie un condensat de 256 bits, soit 32 octets.
  • L’attribut block_size fait référence à la taille du bloc utilisé dans l’algorithme de hachage.
  • L’attribut name est le nom de l’algorithme qu’il est possible d’utiliser avec le constructeur new(). La valeur de cet attribut peut être utile lorsque les objets de hachage n’ont pas de noms descriptifs.

Vérifions ces attributs pour l’objet sha256_hash que nous avons créé précédemment :

>>> sha256_hash.digest_size
32
>>> sha256_hash.block_size
64
>>> sha256_hash.name
'sha256'

Voyons maintenant quelques applications pratiques du hachage à l’aide du module hashlib de Python.

Exemples Pratiques de Hachage

Vérification de l’intégrité des logiciels et des fichiers

En tant que développeurs, nous téléchargeons et installons des paquets logiciels en permanence. Que ce soit sous Linux, Windows ou Mac.

Cependant, certains miroirs de téléchargement de logiciels ne sont pas totalement fiables. Il est donc possible de trouver le condensat (ou la somme de contrôle) à côté du lien de téléchargement, afin de vérifier l’intégrité du logiciel en calculant le condensat et en le comparant au condensat officiel.

Ce principe s’applique également aux fichiers sur votre machine. La moindre modification du contenu d’un fichier aura un impact important sur le condensat. Vous pouvez donc vérifier si un fichier a été modifié en recalculant son condensat.

Voici un exemple simple. Créez un fichier texte nommé « my_file.txt » dans votre répertoire de travail et ajoutez-y du contenu.

$ cat my_file.txt
This is a sample text file.
We are  going to compute the SHA256 hash of this text file and also
check if the file has been modified by
recomputing the hash.

Ouvrez ensuite le fichier en mode lecture binaire (‘rb’), lisez le contenu du fichier et calculez le condensat SHA256 comme suit :

>>> import hashlib
>>> with open("my_file.txt","rb") as file:
...     file_contents = file.read()
...     sha256_hash = hashlib.sha256()
...     sha256_hash.update(file_contents)
...     original_hash = sha256_hash.hexdigest()

Ici, la variable original_hash contient le condensat du fichier « my_file.txt » dans son état actuel.

>>> original_hash
# Résultat: '53bfd0551dc06c4515069d1f0dc715d002d451c8799add29f3e5b7328fda9f8f'

Modifiez maintenant le fichier « my_file.txt ». Vous pouvez par exemple supprimer l’espace supplémentaire devant le mot « going ». 🙂

Calculez à nouveau le condensat et stockez-le dans la variable computed_hash.

>>> import hashlib
>>> with open("my_file.txt","rb") as file:
...     file_contents = file.read()
...     sha256_hash = hashlib.sha256()
...     sha256_hash.update(file_contents)
...     computed_hash = sha256_hash.hexdigest()

Vous pouvez ensuite ajouter une simple instruction assert qui vérifie si computed_hash est égal à original_hash.

>>> assert computed_hash == original_hash

Si le fichier a été modifié (ce qui est le cas ici), vous devriez obtenir une AssertionError :

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Le hachage peut également être utilisé pour protéger les informations confidentielles, telles que les mots de passe stockés dans des bases de données. Il sert également à l’authentification par mot de passe lors de la connexion à des bases de données, en comparant le condensat du mot de passe saisi au condensat du mot de passe correct.

Conclusion

J’espère que ce tutoriel vous a permis de comprendre comment générer des condensats sécurisés en Python. Voici les principaux points à retenir :

  • Le module hashlib de Python met à disposition des implémentations prêtes à l’emploi de nombreux algorithmes de hachage. La liste des algorithmes garantis sur votre plateforme est accessible via hashlib.algorithms_guaranteed.
  • Pour créer un objet de hachage, utilisez le constructeur générique new() selon la syntaxe hashlib.new("nom-de-l-algo"). Il est aussi possible d’utiliser les constructeurs correspondant aux algorithmes de hachage spécifiques, comme hashlib.sha256() pour le hachage SHA 256.
  • Après avoir initialisé la chaîne de caractères à hacher et l’objet de hachage, appliquez la méthode update() sur l’objet de hachage, suivie de la méthode hexdigest() pour obtenir le condensat.
  • Le hachage peut servir à vérifier l’intégrité des logiciels et des fichiers, à protéger des informations confidentielles dans des bases de données, etc.

Pour aller plus loin, apprenez à créer un générateur de mot de passe aléatoire en Python.