在python中使用salt并哈希密码

Chr*_*row 75 python authentication passwords hash salt

这段代码应该用盐哈希密码.salt和哈希密码正在保存在数据库中.密码本身不是.

考虑到操作的敏感性,我想确保一切都是犹太洁食.

注意:我习惯使用url安全版的b64encode.

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())
Run Code Online (Sandbox Code Playgroud)

Chr*_*row 53

根据这个问题的其他答案,我使用bcrypt实现了一种新方法.

为什么要使用bcrypt

如果我理解正确,使用的说法bcryptSHA512bcrypt被设计成缓慢.bcrypt还可以选择在第一次生成散列密码时调整您希望的速度:

# The '12' is the number that dictates the 'slowness'
bcrypt.hashpw(password, bcrypt.gensalt( 12 ))
Run Code Online (Sandbox Code Playgroud)

慢是可取的,因为如果恶意方获取包含散列密码的表,那么对它们进行解密则要困难得多.

履行

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)
Run Code Online (Sandbox Code Playgroud)

笔记

我可以使用以下方法在Linux系统中轻松安装库:

pip install py-bcrypt
Run Code Online (Sandbox Code Playgroud)

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

  • 根据 https://pypi.python.org/pypi/bcrypt/3.1.0,bcrypt 的最大密码长度为 72 字节。**除此之外的任何字符都将被忽略。**因此,他们建议先使用加密哈希函数进行哈希处理,然后对哈希进行 base64 编码(有关详细信息,请参阅链接)。旁注:似乎 `py-bcrypt` 是旧的 pypi 包,后来被重命名为 `bcrypt`。 (4认同)
  • 这在 Python 3.8.5 中对我有用,但我必须将密码编码为字节,如下所示: `plain_text_password.encode('utf-8')` (4认同)
  • 12是gensalt的默认值 (3认同)

M.D*_*.D. 47

聪明的事情不是自己编写加密,而是使用像passlib这样的东西:https://bitbucket.org/ecollins/passlib/wiki/Home

以安全的方式编写加密代码很容易.令人讨厌的是,使用非加密代码,当程序崩溃时,它会在它无法正常工作时立即注意到它.使用加密代码时,您通常只能在迟到之后发现并且您的数据已被泄露.因此,我认为最好使用由其他人知道的关于该主题并且基于战斗测试协议的其他人编写的包.

passlib还有一些很好的功能,使它易于使用,并且如果旧协议被破坏,也很容易升级到更新的密码散列协议.

此外,只有一轮sha512更容易受到字典攻击.sha512设计得很快,这在尝试安全存储密码时实际上是一件坏事.其他人一直在思考所有这些问题,所以你最好利用这个.

  • @dghubble`hashlib`用于加密哈希函数.`passlib`用于安全存储密码.他们不是一回事(虽然很多人似乎都这么认为......然后他们的用户密码被破解了). (16认同)
  • 我想使用crypo库的建议是好的,但OP已经在使用hashlib,这是一个加密库,也在Python标准库中(与passlib不同).如果我处于OP状态,我会继续使用hashlib. (5认同)
  • 如果有人想知道:`passlib`生成自己的salt,它存储在返回的哈希字符串中(至少对于某些方案,如[BCrypt + SHA256](http://pythonhosted.org/passlib/lib/passlib). hash.bcrypt_sha256.html?highlight = format #format)) - 所以你不必担心它. (3认同)

Tay*_*mon 38

编辑:这个答案是错误的.不要使用加密哈希来存储密码.使用密码哈希.


看起来很好我.但是,我很确定你实际上并不需要base64.你可以这样做:

import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()
Run Code Online (Sandbox Code Playgroud)

如果它不会造成困难,您可以通过将salt和哈希密码存储为原始字节而不是十六进制字符串来在数据库中获得稍高效的存储.要做到这一点,更换hexbyteshexdigestdigest.

  • 你没有反转它,你永远不会反转密码.这就是为什么我们哈希它,我们不加密它.如果需要将输入密码与存储的密码进行比较,则可以对输入进行哈希处理并比较哈希值.如果您使用密钥加密密码,则可以解密密码并查看密码.这不安全 (17认同)
  • uuid.uuid4().hex每次生成时都不同.如果您无法获得相同的uuid,您将如何比较密码以进行检查? (3认同)
  • @LittleBobbyTables ,salt 值与用户名和他/她相应密码的散列值一起存储在数据库中。当用户输入密码时,它实际上被散列,这个散列值与存储在数据库中的盐值连接。如果匹配,则认为登录成功。 (3认同)
  • 如果答案错误 - 删除它! (3认同)
  • @LittleBobbyTables我认为`salt`存储在数据库中,也是咸的哈希密码. (2认同)

Mar*_*ery 21

从 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')
Run Code Online (Sandbox Code Playgroud)

注意:

  • 16 字节盐的使用和 100000 次 PBKDF2 迭代与 Python 文档中推荐的最小数量相匹配。进一步增加迭代次数将使您的哈希计算速度变慢,因此更安全。
  • os.urandom 始终使用加密安全的随机源
  • hmac.compare_digest中使用的is_correct_password, 基本上只是==字符串的运算符,但没有短路的能力,这使得它不受计时攻击的影响。这可能并没有真正提供任何额外的安全价值,但它也没有伤害,所以我继续使用它。

有关什么是好的密码散列的理论以及适用于散列密码的其他函数列表,请参阅https://security.stackexchange.com/q/211/29805


小智 19

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

hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()
Run Code Online (Sandbox Code Playgroud)

否则你会得到:

回溯(最近一次调用最后一次):
文件"",第1行,在
hashed_pa​​ssword = hashlib.sha512(密码+盐).hexdigest()
TypeError:必须在散列之前对Unicode对象进行编码

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

Ter*_*way 10

如果您需要使用现有系统存储的哈希值,则passlib似乎很有用.如果您可以控制格式,请使用像bcrypt或scrypt这样的现代哈希.这时,bcrypt似乎更容易从python中使用.

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

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

另请参见:如何使用scrypt在Python中生成密码和salt的哈希值


nag*_*lzs 6

我不想复活旧线程,但是......任何想要使用现代最新安全解决方案的人都会使用argon2.

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

它赢得了密码哈希竞争.(https://password-hashing.net/)它比bcrypt更容易使用,并且比bcrypt更安全.