Python 3 中的随机盐字符串

random salt string in Python 3

我正在尝试将以下内容迁移到 Python 3.

def mkhash(password, salt=None):
    """
    Compute SHA256 hash of password with pbkdf2 algorithm.
    Call with salt=None for creating hash. To compute verification
    hash, supply salt stored in the user's row in auth_user.
    Args:
        password :
        salt=None :
    Returns: tuple (hash, salt)
    Raises:  Nothing
    """
    if salt is None:
        ## use a 16 char random string
        randchars = [random.choice(string.ascii_lowercase) for _ in range(16)]
        #salt = b''.join(randchars)# works in 2 but not 3
        salt = ''.join(randchars)  # works in 3 but result fails in hashlib call

    # See https://docs.python.org/2/library/hashlib.html
    dk = hashlib.pbkdf2_hmac('sha256', password, salt, 10000)
    pwhash = binascii.hexlify(dk)
    return (pwhash, salt)        

这是 Python 3.

中失败的回溯
Traceback (most recent call last):
  File "auth.py", line 451, in <module>
    _ = mkhash('badpassword')
  File "auth.py", line 146, in mkhash
    dk = hashlib.pbkdf2_hmac('sha256', password, salt, 10000)
TypeError: a bytes-like object is required, not 'str'

在 Python 3 中,生成与 hashlib 函数兼容的长度为 N 的盐的正确方法是什么?

编辑:使用已接受答案的工作版本:

def mkhash(password, salt=None):
    """
    Compute SHA256 hash of password with pbkdf2 algorithm.
    Call with salt=None for creating hash. To compute verification
    hash, supply salt stored in the user's row in auth_user.
    Args:
        password :
        salt=None :
    Returns: tuple (hash, salt)
    Raises:  Nothing
    """
    if salt is None:
        salt = os.urandom(16)
    elif type(salt) is not bytes:
        salt = salt.encode('utf-8')

    # See https://docs.python.org/3/library/hashlib.html
    dk = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 10000)
    pwhash = binascii.hexlify(dk)
    return (pwhash, salt)                   

您可以使用 .encode() 将字符串对象转换为字节。

salt = salt.encode('utf-8')

但是你不应该

random 模块不生成加密安全随机数。这会在您的代码中留下一个漏洞。如果您使用 Python 3.6,secrets 模块更好。

salt = secrets.token_bytes(16)

如果不是,os.urandom() 也被记录为 "unpredictable enough for cryptographic applications"。

salt = os.urandom(16)