137 votes

Saler et hacher un mot de passe en Python

Ce code est censé hacher un mot de passe avec un sel. Le sel et le mot de passe haché sont sauvegardés dans la base de données. Le mot de passe lui-même ne l'est pas.

Étant donné la nature sensible de l'opération, je voulais m'assurer que tout était kasher.

import hashlib
import base64
import uuid

password = 'test_password'
salt     = base64.urlsafe_b64encode(uuid.uuid4().bytes)

t_sha = hashlib.sha512()
t_sha.update(password+salt)
hashed_password =  base64.urlsafe_b64encode(t_sha.digest())

0 votes

Pourquoi encodez-vous le sel en b64 ? Il serait plus simple d'utiliser directement le sel et de coder les deux ensemble en b64. t_sha.digest() + salt . Vous pouvez à nouveau séparer le sel plus tard lorsque vous avez décodé le mot de passe salé, car vous savez que le mot de passe haché décodé est exactement de 32 octets.

1 votes

@Duncan - J'ai encodé le sel en base64 pour pouvoir effectuer des opérations fortes sur celui-ci sans avoir à me soucier de problèmes bizarres. La version "bytes" fonctionnera-t-elle comme une chaîne de caractères ? Si c'est le cas, alors je n'ai pas besoin de coder en base64 t_sha.digest() non plus. Je n'enregistrerais probablement pas le mot de passe haché et le sel ensemble, car cela semble un peu plus compliqué et un peu moins lisible.

0 votes

Si vous utilisez Python 2.x, l'objet bytes fonctionnera parfaitement comme une chaîne de caractères. Python n'impose aucune restriction sur ce que vous pouvez avoir dans une chaîne de caractères. Toutefois, il n'en va pas de même si vous transmettez la chaîne à un code externe tel qu'une base de données. Python 3.x fait la distinction entre les types d'octets et les chaînes de caractères. Dans ce cas, vous ne voudrez pas utiliser les opérations de chaîne de caractères sur le sel.

113voto

Chris Dutrow Points 8662

Sur la base des autres réponses à cette question, j'ai mis en place une nouvelle approche utilisant bcrypt.

Pourquoi utiliser bcrypt

Si je comprends bien, l'argument pour utiliser bcrypt sur SHA512 c'est que bcrypt est conçu pour être lent. bcrypt dispose également d'une option permettant d'ajuster la vitesse à laquelle vous souhaitez qu'il soit lent lors de la génération du mot de passe haché pour la première fois :

# The '12' is the number that dictates the 'slowness'
bcrypt.hashpw(password, bcrypt.gensalt( 12 ))

La lenteur est souhaitable car si une partie malveillante met la main sur la table contenant les mots de passe hachés, il est alors beaucoup plus difficile de les forcer par force brute.

Mise en œuvre

def get_hashed_password(plain_text_password):
    # Hash a password for the first time
    #   (Using bcrypt, the salt is saved into the hash itself)
    return bcrypt.hashpw(plain_text_password, bcrypt.gensalt())

def check_password(plain_text_password, hashed_password):
    # Check hashed password. Using bcrypt, the salt is saved into the hash itself
    return bcrypt.checkpw(plain_text_password, hashed_password)

Notes

J'ai pu installer la bibliothèque assez facilement dans un système linux en utilisant :

pip install py-bcrypt

Cependant, j'ai eu plus de mal à l'installer sur mes systèmes Windows. Il semble avoir besoin d'un correctif. Voir cette question sur Stack Overflow : py-bcrypt s'installe sur win 7 64bit python

7 votes

12 est la valeur par défaut de gensalt

7 votes

Selon pypi.python.org/pypi/bcrypt/3.1.0 La longueur maximale du mot de passe pour bcrypt est de 72 octets. Tout autre caractère est ignoré. C'est pourquoi ils recommandent de procéder d'abord à un hachage avec une fonction de hachage cryptographique, puis de coder le hachage en base64 (voir le lien pour plus de détails). Remarque annexe : Il semble que py-bcrypt est l'ancien paquet pypi et a depuis été renommé en bcrypt .

1 votes

Hashing est ok, mais check_password donne toujours false dans djano

60voto

Taymon Points 8103

EDIT : Cette réponse est fausse. Une seule itération de SHA512 est égale à rapide ce qui le rend inapproprié pour une utilisation comme fonction de hachage de mot de passe. Utilisez plutôt l'une des autres réponses proposées ici.


Ça m'a l'air bien. Cependant, je suis presque sûr que vous n'avez pas besoin de base64. Tu pourrais juste faire ça :

import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()

Si cela ne crée pas de difficultés, vous pouvez obtenir un stockage légèrement plus efficace dans votre base de données en stockant le sel et le mot de passe haché sous forme d'octets bruts plutôt que de chaînes hexadécimales. Pour ce faire, remplacez hex con bytes y hexdigest con digest .

1 votes

Oui, un hexagone ferait très bien l'affaire. Je préfère la base64 parce que les chaînes sont un peu plus courtes. Il est plus efficace de faire circuler et d'effectuer des opérations sur des chaînes plus courtes.

0 votes

Maintenant, comment l'inverser pour récupérer le mot de passe ?

35 votes

On ne l'inverse pas, on n'inverse jamais un mot de passe. C'est pourquoi nous le hachons et nous ne le cryptons pas. Si vous devez comparer un mot de passe entré avec un mot de passe stocké, vous hacherez l'entrée et comparerez les hachages. Si vous cryptez un mot de passe, toute personne possédant la clé peut le décrypter et le voir. Ce n'est pas sûr

48voto

koffie Points 460

Editar:

La bibliothèque suggérée dans cette réponse est maintenant dépassée, et la fonctionnalité de dérivation de clés hashlib mentionnés dans cette réponse : https://stackoverflow.com/a/56915300/893857 est une bonne suggestion à utiliser de nos jours.

Réponse originale L'idéal n'est pas d'écrire la cryptographie vous-même mais d'utiliser quelque chose comme passlib : https://passlib.readthedocs.io/en/stable/#

Il est facile de se tromper en écrivant son code cryptographique de manière sécurisée. Ce qui est désagréable, c'est qu'avec un code non cryptographique, on s'aperçoit souvent immédiatement qu'il ne fonctionne pas et que le programme plante. Alors qu'avec un code cryptographique, vous ne vous en rendez compte qu'après qu'il soit trop tard et que vos données aient été compromises. C'est pourquoi je pense qu'il est préférable d'utiliser un paquet écrit par quelqu'un d'autre qui connaît bien le sujet et qui est basé sur des protocoles éprouvés.

De plus, passlib dispose de quelques fonctionnalités intéressantes qui le rendent facile à utiliser et à mettre à niveau vers un protocole de hachage de mot de passe plus récent si l'ancien protocole s'avère défaillant.

De plus, un seul tour de sha512 est plus vulnérable aux attaques par dictionnaire. sha512 est conçu pour être rapide et c'est en fait une mauvaise chose lorsqu'on essaie de stocker des mots de passe en toute sécurité. D'autres personnes ont longuement réfléchi à toutes ces questions et vous devriez en tirer parti.

5 votes

Je suppose que le conseil d'utiliser des bibliothèques cryptographiques est bon, mais le PO utilise déjà hashlib, une bibliothèque cryptographique qui est également dans la bibliothèque standard de Python (contrairement à passlib). Je continuerais à utiliser hashlib si j'étais dans la situation de l'OP.

19 votes

@dghubble hashlib est pour les fonctions de hachage cryptographiques. passlib sert à stocker en toute sécurité les mots de passe. Ce n'est pas la même chose (bien que beaucoup de gens semblent le penser et ensuite les mots de passe de leurs utilisateurs sont piratés).

4 votes

Au cas où quelqu'un se demanderait : passlib génère son propre sel, qui est stocké dans la chaîne de hachage retournée (au moins pour certains schémas tels que le BCrypt+SHA256 ) - vous n'avez donc pas à vous en soucier.

27voto

Wayne Points 61

Pour que cela fonctionne dans Python 3, vous devrez encoder UTF-8 par exemple :

hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()

Sinon, vous obtiendrez :

Traceback (dernier appel le plus récent) :
Fichier "", ligne 1, dans
hashed_password = hashlib.sha512(password + salt).hexdigest()
Erreur de type : Les objets Unicode doivent être encodés avant d'être hachés.

10 votes

Non. N'utilisez pas de fonction de hachage sha pour hacher des mots de passe. Utilisez quelque chose comme bcrypt. Voir les commentaires aux autres questions pour la raison.

0 votes

@josch Quels commentaires ?

0 votes

@CoolCloud ctrl+f "Pourquoi utiliser bcrypt"

12voto

Teris Riel Points 156

Passlib semble être utile si vous avez besoin d'utiliser des hachages stockés par un système existant. Si vous avez le contrôle du format, utilisez un hachage moderne comme bcrypt ou scrypt. Pour l'instant, bcrypt semble être beaucoup plus facile à utiliser depuis python.

passlib supporte bcrypt, et il recommande d'installer py-bcrypt comme backend : http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

Vous pouvez également utiliser py-bcrypt directement si vous ne voulez pas installer passlib. Le fichier readme contient des exemples d'utilisation de base.

voir aussi : Comment utiliser scrypt pour générer un hash pour un mot de passe et un sel en Python ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X