web-dev-qa-db-fra.com

Salez et hachez un mot de passe dans python

Ce code est supposé hacher un mot de passe avec un sel. Le sel et le mot de passe haché sont en cours d'enregistrement dans la base de données. Le mot de passe lui-même n'est pas.

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

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())
73
Chris Dutrow

EDIT: Cette réponse est fausse. Une seule itération de SHA512 est rapide , ce qui la rend inappropriée pour une utilisation en tant que fonction de hachage de mot de passe. Utilisez plutôt l’une des autres réponses ici.


Ça a l'air bien par moi. Cependant, je suis presque sûr que vous n'avez pas besoin de base64. Vous pouvez simplement faire ceci:

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 mot de passe salt et hashed sous forme d'octets bruts plutôt que de chaînes hexadécimales. Pour ce faire, remplacez hex par bytes et hexdigest par digest.

38
Taymon

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

Pourquoi utiliser bcrypt

Si je comprends bien, l'argument pour utiliser bcrypt sur SHA512 est que bcrypt est conçu pour être lent. bcrypt a également une option pour ajuster la lenteur souhaitée lors de la première génération du mot de passe haché:

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

Lent est souhaitable car si une partie malveillante met la main sur une table contenant des mots de passe hachés, il est beaucoup plus difficile de la forcer brutalement.

La mise en oeuvre

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)

Remarques

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 difficulté à l'installer sur mes systèmes Windows. Il semble avoir besoin d'un patch. Consultez cette question relative au débordement de la pile: installation de py-bcrypt sur un python Win 7 64 bits

50
Chris Dutrow

L’intelligence n’est pas d’écrire la crypto vous-même, mais d’utiliser quelque chose comme: passlib: https://bitbucket.org/ecollins/passlib/wiki/Home

Il est facile de gâcher l'écriture de votre code crypté de manière sécurisée. Le problème, c'est qu'avec un code non crypté, vous le remarquez immédiatement quand il ne fonctionne pas car votre programme se bloque. Bien qu'avec le code crypté, vous ne le découvriez souvent que trop tard et que vos données ont été compromises. Par conséquent, je pense qu’il est préférable d’utiliser un paquetage écrit par quelqu'un d’autre qui connaît le sujet et qui est basé sur des protocoles testés au combat.

En outre, passlib présente certaines fonctionnalités de Nice qui le rendent facile à utiliser et à mettre à niveau vers un nouveau protocole de hachage de mot de passe si un ancien protocole s'avère défectueux.

De plus, un seul cycle 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 lorsque vous essayez de stocker des mots de passe en toute sécurité. D'autres personnes ont longuement réfléchi à toutes ces questions afin que vous en profitiez mieux.

46
M.D.

Pour que cela fonctionne Python 3, vous aurez besoin d'encoder UTF-8 par exemple:

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

Sinon, vous aurez:

Traceback (appel le plus récent en dernier):
Fichier "", ligne 1, dans
hashed_password = hashlib.sha512 (mot de passe + sel) .hexdigest ()
TypeError: les objets Unicode doivent être codés avant le hachage

18
Wayne

passlib semble être utile si vous devez utiliser des hachages stockés par un système existant. Si vous avez le contrôle du format, utilisez un hachage moderne tel que bcrypt ou scrypt. À ce stade, bcrypt semble être beaucoup plus facile à utiliser à partir de python.

passlib supporte bcrypt, et recommande d'installer py-bcrypt en tant que serveur: http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

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

voir aussi: Comment utiliser scrypt pour générer du hash pour mot de passe et salt en Python

10
Terrel Shumway

Je ne veux pas ressusciter un ancien fil de discussion, mais si vous souhaitez utiliser une solution sécurisée moderne et moderne, utilisez argon2.

https://pypi.python.org/pypi/argon2_cffi

Il a remporté le concours de hachage de mots de passe. ( https://password-hashing.net/ ) Il est plus facile à utiliser que bcrypt et il est plus sécurisé que bcrypt.

6
nagylzs

Tout d'abord importer: -

import hashlib, uuid

Puis changez votre code en fonction de cela dans votre méthode:

uname = request.form["uname"]
pwd=request.form["pwd"]
salt = hashlib.md5(pwd.encode())

Puis passez ce salt et uname dans votre requête SQL de base de données, en-dessous de login, vous trouverez un nom de table:

sql = "insert into login values ('"+uname+"','"+email+"','"+salt.hexdigest()+"')"
0
Sheetal Jha

A partir de Python 3.4, le module hashlib de la bibliothèque standard contient dérivation de clé des fonctions qui sont " pour le hachage de mot de passe sécurisé ".

Donc utilisez un de ceux-là, comme hashlib.pbkdf2_hmac , avec un sel généré en utilisant os.urandom :

from typing import Tuple
import os
import hashlib
import hmac

def hash_new_password(password: str) -> Tuple[bytes, bytes]:
    """
    Hash the provided password with a randomly-generated salt and return the
    salt and hash to store in the database.
    """
    salt = os.urandom(16)
    pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    return salt, pw_hash

def is_correct_password(salt: bytes, pw_hash: bytes, password: str) -> bool:
    """
    Given a previously-stored salt and hash, and a password provided by a user
    trying to log in, check whether the password is correct.
    """
    return hmac.compare_digest(
        pw_hash,
        hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    )

# Example usage:
salt, pw_hash = hash_new_password('correct horse battery staple')
assert is_correct_password(salt, pw_hash, 'correct horse battery staple')
assert not is_correct_password(salt, pw_hash, 'Tr0ub4dor&3')
assert not is_correct_password(salt, pw_hash, 'rosebud')

Notez que:

  • L'utilisation d'un sel de 16 octets et de 100 000 itérations de PBKDF2 correspond aux nombres minimums recommandés dans les docs Python. Augmenter encore le nombre d'itérations ralentira le calcul de vos hachages, et donc augmentera la sécurité. .
  • os.urandom utilise toujours une source aléatoire de sécurité cryptographique
  • hmac.compare_digest , utilisé dans is_correct_password, est fondamentalement juste le == opérateur pour les chaînes mais sans possibilité de court-circuit, ce qui le rend insensible aux attaques par le timing. Ce ne fournit probablement pas vraiment de valeur de sécurité supplémentaire , mais ça ne fait pas de mal non plus, alors je suis allé de l'avant et je l'ai utilisé.

Pour connaître la théorie sur ce qui constitue un hachage de mot de passe correct et une liste d'autres fonctions appropriées pour le hachage de mots de passe, voir https://security.stackexchange.com/q/211/29805 .

0
Mark Amery