问题:在Python中加盐并哈希密码
该代码应该用盐来散列密码。盐和哈希密码将保存在数据库中。密码本身不是。
鉴于该操作的敏感性,我想确保所有内容都是洁净的。
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())
回答 0
编辑:这个答案是错误的。SHA512的单次迭代速度很快,这使其不适合用作密码哈希函数。请在此处使用其他答案之一。
我看起来不错。但是,我敢肯定您实际上并不需要base64。您可以这样做:
import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()
如果这不会造成麻烦,则可以通过将salt和哈希密码存储为原始字节而不是十六进制字符串,从而在数据库中获得稍微更有效的存储。要做到这一点,更换hex
用bytes
和hexdigest
用digest
。
回答 1
基于此问题的其他答案,我使用bcrypt实现了一种新方法。
为什么要使用bcrypt
如果我理解正确,使用的说法bcrypt
在SHA512
是bcrypt
被设计成缓慢。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
回答 2
聪明的事不是自己写加密货币,而是使用类似passlib的东西:https ://bitbucket.org/ecollins/passlib/wiki/Home
以安全的方式编写密码很容易造成混乱。令人讨厌的是,使用非加密代码时,由于程序崩溃,当它不起作用时,您经常会立即注意到它。使用密码时,您通常只会发现到很晚才发现您的数据已遭到破坏。因此,我认为最好使用由其他人编写的软件包,该软件包基于经过战斗力测试的协议,对此问题有一定的了解。
passlib还具有一些不错的功能,可以使它易于使用,并且如果原来的协议被破坏,还可以轻松升级到更新的密码哈希协议。
同样,只有一轮sha512更容易受到字典攻击。sha512的设计速度很快,而在尝试安全存储密码时,这实际上是一件坏事。其他人已经对所有此类问题进行了漫长而艰难的思考,因此您最好利用这一点。
回答 3
为了使它在Python 3中工作,您需要使用UTF-8编码,例如:
hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()
否则,您将获得:
追溯(最近一次通话最近):
文件“”,第1行,在
hashed_password = hashlib.sha512(password + salt).hexdigest()
TypeError:Unicode对象必须在散列之前编码
回答 4
从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。
回答 5
如果需要使用现有系统存储的哈希,passlib似乎很有用。如果您可以控制格式,请使用现代的哈希,例如bcrypt或scrypt。目前,bcrypt在python中似乎更容易使用。
passlib支持bcrypt,建议安装py-bcrypt作为后端:http : //pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html
如果您不想安装passlib,也可以直接使用py-bcrypt。自述文件包含一些基本用法示例。
回答 6
我不想复活旧线程,但是…任何想使用现代最新安全解决方案的人都可以使用argon2。
https://pypi.python.org/pypi/argon2_cffi
它赢得了密码哈希竞赛。(https://password-hashing.net/)比bcrypt更易于使用,并且比bcrypt更安全。
回答 7
首先导入:
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()+"')"