在python中使用salt并哈希密码

问题描述 投票:71回答:7

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

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

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())
python authentication hash passwords salt
7个回答
37
投票

编辑:这个答案是错误的。 SHA512的单次迭代很快,这使得它不适合用作密码散列函数。请改用其中一个答案。


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

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

如果它不会造成困难,您可以通过将salt和哈希密码存储为原始字节而不是十六进制字符串来在数据库中获得稍高效的存储。为此,用hex替换byteshexdigest替换digest


49
投票

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

Why use bcrypt

如果我理解正确,使用bcrypt而不是SHA512的论点是bcrypt设计得很慢。 bcrypt还可以选择在第一次生成散列密码时调整您希望的速度:

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

慢是可取的,因为如果恶意方在包含散列密码的表上获取,那么暴力强制它们要困难得多。

Implementation

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

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

pip install py-bcrypt

但是,我在Windows系统上安装它时遇到了更多麻烦。它似乎需要一个补丁。请参阅此Stack Overflow问题:py-bcrypt installing on win 7 64bit python


46
投票

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

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

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

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


18
投票

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

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

否则你会得到:

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


10
投票

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

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

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

另见:How to use scrypt to generate hash for password and salt in Python


6
投票

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

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

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


0
投票

从Python 3.4开始,标准库中的hashlib模块包含“为安全密码散列而设计”的key derivation函数。

因此,使用其中一个,如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字节盐和100000次迭代的PBKDF2匹配Python文档中建议的最小数量。进一步增加迭代次数会使您的哈希计算更慢,因此更安全。
  • os.urandom始终使用加密安全的随机源
  • hmac.compare_digest中使用的is_correct_password,基本上只是==字符串的操作符,但没有短路能力,这使它免受定时攻击。那probably doesn't really provide any extra security value,但它也没有受伤,所以我继续使用它。

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


-1
投票

首先进口: -

import hashlib, uuid

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

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

然后在你的数据库sql查询中传递这个salt和uname,下面登录的是一个表名:

sql = "insert into login values ('"+uname+"','"+email+"','"+salt.hexdigest()+"')"
© www.soinside.com 2019 - 2024. All rights reserved.