在Python中加盐并哈希密码


97

该代码应该用盐来散列密码。盐和哈希密码将保存在数据库中。密码本身不是。

鉴于该操作的敏感性,我想确保所有内容都是洁净的。

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())

您为什么要对盐进行b64编码?直接使用盐,然后将b64一起编码,会更简单t_sha.digest() + salt。您可以在对盐腌的哈希密码进行解码后,再将盐分出去,因为您知道解码后的哈希密码正好是32个字节。
邓肯2012年

1
@Duncan-我对base64进行了编码,因此我可以对其进行强大的操作,而不必担心奇怪的问题。“字节”版本是否可以作为字符串使用?如果真是这样,那么我也不需要base64编码t_sha.digest()。我可能不会仅将散列的密码和盐保存在一起,因为这看起来有点复杂且可读性较差。
克里斯·杜特罗

如果您使用的是Python 2.x,则bytes对象可以很好地用作字符串。Python对字符串中的内容没有任何限制。但是,如果将字符串传递给任何外部代码(例如数据库),则可能不适用。Python 3.x区分字节类型和字符串,因此在这种情况下,您不希望对salt使用字符串操作。
邓肯

4
我无法告诉您如何在python中执行此操作,但是普通的SHA-512是一个不好的选择。使用慢散列,例如PBKDF2,bcrypt或scrypt。
CodesInChaos 2012年

旁注:我建议不要使用UUID作为加密随机性的来源。是的,CPython使用的实现在密码上是安全的,但这不是Python的规范或UUID规范所规定的,并且存在易受攻击的实现。如果您的代码库是使用不带安全UUID4的Python实现运行的,那么您的安全性将受到削弱。这可能是不太可能发生的情况,但是使用它secrets却不花钱。
Mark Amery

Answers:


49

编辑:这个答案是错误的。SHA512的单次迭代速度很快,这使其不适合用作密码哈希函数。请在此处使用其他答案之一。


我看起来不错。但是,我敢肯定您实际上并不需要base64。您可以这样做:

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

如果这不会造成麻烦,则可以通过将salt和哈希密码存储为原始字节而不是十六进制字符串,从而在数据库中获得稍微更有效的存储。要做到这一点,更换hexbyteshexdigestdigest


1
是的,十六进制可以正常工作。我更喜欢base64,因为字符串要短一些。在较短的字符串上传递和执行操作效率更高。
克里斯·杜特罗

现在,如何将其取反以找回密码?
nodebase 2014年

28
您不撤消密码,也永不撤消密码。这就是为什么我们对它进行散列而不加密的原因。如果需要将输入密码与存储的密码进行比较,则可以对输入进行散列并比较散列。如果您对密码进行加密,则任何拥有密钥的人都可以解密并看到它。这并不安全
塞巴斯蒂安·加布里埃尔·芬奇

4
uuid.uuid4()。hex每次生成都不同。如果无法找回相同的uuid,您将如何比较密码以进行检查?
LittleBobbyTables

3
我认为@LittleBobbyTablessalt也存储在数据库中,并且也包含咸哈希密码。
clemtoy 2015年

72

基于此问题的其他答案,我使用bcrypt实现了一种新方法。

为什么要使用bcrypt

如果我理解正确,使用的说法bcryptSHA512bcrypt被设计成缓慢。bcrypt还提供了一个选项,用于调整首次生成哈希密码时的速度:

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

缓慢是可取的,因为如果恶意方将他们的手放到包含哈希密码的表上,那么蛮力地将它们强行加起来就困难得多。

实作

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)

笔记

我能够使用以下命令在Linux系统中轻松安装该库:

pip install py-bcrypt

但是,我在Windows系统上安装它时遇到了更多麻烦。它似乎需要一个补丁。看到这个Stack Overflow问题:在Win 7 64位python上安装py-bcrypt


4
12是gensalt的默认值
Ahmed Hegazy

2
根据pypi.python.org/pypi/bcrypt/3.1.0,bcrypt的最大密码长度为72个字节。超出此范围的任何字符均将被忽略。因此,他们建议先使用加密哈希函数进行哈希,然后再对哈希进行base64编码(有关详细信息,请参见链接)。旁注:似乎py-bcrypt是旧的pypi软件包,此后已重命名为bcrypt
balu

48

聪明的事不是自己写加密货币,而是使用类似passlib的东西:https ://bitbucket.org/ecollins/passlib/wiki/Home

以安全的方式编写密码很容易造成混乱。令人讨厌的是,使用非加密代码时,由于程序崩溃,当它不起作用时,您经常会立即注意到它。使用密码时,您通常只会发现到很晚才发现您的数据已遭到破坏。因此,我认为最好使用由其他人编写的软件包,该软件包基于经过战斗力测试的协议,对此问题有一定的了解。

passlib还具有一些不错的功能,可以使它易于使用,并且如果原来的协议被破坏,还可以轻松升级到更新的密码哈希协议。

同样,只有一轮sha512更容易受到字典攻击。sha512的设计速度很快,而在尝试安全存储密码时,这实际上是一件坏事。其他人已经对所有此类问题进行了漫长而艰难的思考,因此您最好利用这一点。


5
我认为使用crypo库的建议很好,但是OP已经在使用hashlib,这是一个加密库,也位于Python标准库中(与passlib不同)。如果处于OP情况,我将继续使用hashlib。
dgh 2012年

18
@dghubblehashlib用于加密哈希函数。passlib用于安全存储密码。他们不是同一件事(尽管很多人似乎是这样认为..然后他们的用户密码被破解了)。
布伦丹·朗

3
如果有人想知道:passlib生成自己的盐,将其存储在返回的哈希字符串中(至少对于某些方案,例如BCrypt + SHA256),因此您不必担心。
z0r

22

为了使它在Python 3中工作,您需要使用UTF-8编码,例如:

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

否则,您将获得:

追溯(最近一次通话最近):
文件“”,第1行,在
hashed_pa​​ssword = hashlib.sha512(password + salt).hexdigest()
TypeError:Unicode对象必须在散列之前编码


7
否。请勿使用任何sha哈希函数来哈希密码。使用类似bcrypt之类的东西。请参阅对其他问题的评论以了解原因。
josch

13

从Python 3.4开始,hashlib标准库中的模块包含“被设计用于安全密码散列”的密钥派生函数。

因此,请使用其中一种,例如hashlib.pbkdf2_hmac,使用以下方法生成的盐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')

注意:

  • 使用16字节盐和PBKDF2的100000迭代与Python文档中建议的最小数目相匹配。进一步增加迭代次数将使散列的计算速度变慢,因此更加安全。
  • os.urandom 始终使用加密安全的随机源
  • hmac.compare_digest在中使用的is_correct_password,基本上只是==字符串的运算符,但没有短路能力,这使其不受定时攻击的影响。那可能实际上并没有提供任何额外的安全性价值,但是也没有什么坏处,所以我继续使用它。

有关如何进行良好的密码哈希处理的理论以及适用于对密码进行哈希处理的其他功能的列表,请参见https://security.stackexchange.com/q/211/29805


11

如果需要使用现有系统存储的哈希,passlib似乎很有用。如果您可以控制格式,请使用现代的哈希,例如bcrypt或scrypt。目前,bcrypt在python中似乎更容易使用。

passlib支持bcrypt,建议安装py-bcrypt作为后端:http : //pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

如果您不想安装passlib,也可以直接使用py-bcrypt。自述文件包含一些基本用法示例。

另请参阅:如何在Python中使用scrypt为密码和盐生成哈希



0

首先导入:

import hashlib, uuid

然后根据您的方法更改此代码:

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

然后在数据库sql查询中传递此salt和uname,在login下面是一个表名:

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

uname = request.form [“ uname”] pwd = request.form [“ pwd”] salt = hashlib.md5(pwd.encode())然后在您的数据库sql查询中传递此salt和uname,登录名是表名:-sql =“插入登录值('” + uname +“','” + email +“','” + salt.hexdigest()+“')”
Sheetal Jha

-1是因为md5非常快,这使得使用md5的单个迭代成为密码哈希函数的不佳选择。
Mark Amery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.