使用 Python 对密码进行加盐和哈希处理
- 2025-03-11 08:54:00
- admin 原创
- 65
问题描述:
此代码旨在使用盐对密码进行哈希处理。盐和哈希密码均保存在数据库中。但密码本身则不保存。
鉴于该操作的敏感性,我想确保一切都能正常执行。
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())
解决方案 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 问题:在 Win7 64 位 Python 上安装 py-bcrypt
解决方案 2:
编辑:这个答案是错误的。SHA512 的单次迭代速度很快,因此不适合用作密码哈希函数。请使用此处的其他答案之一。
我觉得没问题。但是,我很确定你实际上不需要 base64。你可以这样做:
import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()
如果不造成困难,您可以通过将盐和散列密码存储为原始字节而不是十六进制字符串来在数据库中获得稍微更高效的存储。为此,请将 替换hex
为bytes
并将hexdigest
替换为digest
。
解决方案 3:
编辑:
这个答案中建议的库现在已经过时了,并且这个答案中提到的hashlib 密钥派生功能:https://stackoverflow.com/a/56915300/893857是现在使用的好建议。
原始答案
明智的做法是不要自己编写加密,而是使用类似 passlib 的东西:https://passlib.readthedocs.io/en/stable/#
以安全的方式编写加密代码很容易搞砸。糟糕的是,使用非加密代码时,由于程序崩溃,您通常会立即注意到它无法正常工作。而使用加密代码时,您通常只能在为时已晚并且数据已被泄露后才发现。因此,我认为最好使用由熟悉该主题且基于经过实战测试的协议的其他人编写的软件包。
此外,passlib 还具有一些很好的特性,使其易于使用,并且如果旧协议被破坏,也易于升级到较新的密码哈希协议。
此外,仅进行一轮 sha512 更容易受到字典攻击。sha512 的设计目标是快速,而这在尝试安全存储密码时实际上是一件坏事。其他人已经对所有这类问题进行了长期而深入的思考,因此您最好利用这一点。
解决方案 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 字节盐和 100000 次 PBKDF2 迭代符合 Python 文档中推荐的最小数量。进一步增加迭代次数将使您的哈希计算速度变慢,因此更安全。
os.urandom
始终使用加密安全的随机源hmac.compare_digest
中使用的is_correct_password
基本上只是==
字符串运算符,但没有短路功能,这使得它不受时序攻击的影响。这可能并没有真正提供任何额外的安全价值,但也没有坏处,所以我继续使用它。
有关构成良好密码哈希的理论以及适合对密码进行哈希处理的其他函数列表,请参阅https://security.stackexchange.com/q/211/29805。
解决方案 5:
为了使其在 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 对象必须在散列之前进行编码
解决方案 6:
如果您需要使用现有系统存储的哈希值,passlib 似乎很有用。如果您可以控制格式,请使用 bcrypt 或 scrypt 等现代哈希值。目前,bcrypt 似乎更容易从 python 使用。
passlib 支持 bcrypt,建议安装 py-bcrypt 作为后端:http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html
如果不想安装 passlib,也可以直接使用py-bcrypt 。自述文件中有基本使用的示例。
另请参阅:如何在 Python 中使用 scrypt 生成密码和盐的哈希值
解决方案 7:
我不想重新讨论旧话题,但是……任何想要使用现代最新安全解决方案的人都可以使用 argon2。
https://pypi.python.org/pypi/argon2_cffi
它赢得了密码哈希计算竞赛。(https://password-hashing.net/)它比 bcrypt 更易于使用,并且比 bcrypt 更安全。
解决方案 8:
import bcrypt
password = "You know my name"
salt = bcrypt.gensalt(2)
hashed = bcrypt.hashpw(password, salt)
hashed2 = bcrypt.hashpw("password1", hashed)
if hashed2 == hashed:
print("It matches")
else:
print("It does not match")
参考:https://github.com/erlichmen/py-bcrypt/blob/master/simple_test.py
pip install py-bcrypt
解决方案 9:
我以前在 NodeJs 中做过同样的事情:
echo "console.log(require('crypto').createHmac('sha256', 'salt').update('password').digest('hex'))" | node
它在 Python 中的等效版本是:
python3 -c 'import hashlib;import base64;import hmac;print(hmac.new(b"salt", "password".encode(), hashlib.sha256).hexdigest())'
等效的 shell 命令是:
echo -n "password" | openssl sha256 -hmac "salt"
解决方案 10:
我个人会使用“swiftcrypt”模块。
pip install swiftcrypt
出于安全相关的原因,它具有许多功能。
import swiftCrypt
password= "coolPassword here"
salt = swiftCrypt.Salts().generate_salt(14)
hashedPass = swiftCrypt.Hash().hash_password(password,salt,"sha256")
print(hashedPass)
您可以生成具有自定义长度的盐,如果您想要随机长度,只需将其留空。
它将使用盐和您选择的算法创建一个散列密码。
然后您可以像这样验证密码:
verifiedPass = swiftCrypt.Checker().verify_password(password, hashedPass, salt,"sha256")
if verifiedPass == True:
print("Password is correct!")
else:
print("Password is incorrect!")
该verify_password
方法采用用户输入的密码、存储的散列密码、用于创建密码的盐以及使用的算法。
扫码咨询,免费领取项目管理大礼包!