1. os 生成salt ---> (盐) --->(字节)
import os
print(os.urandom(16))
# 输出字节: b'\xc0Y\xc3`\x8aK\x0c\x18\x9ay\x1f\xe0\xe06\xc6\xb6'
2. secrets生成salt ---> (盐) --->(字节)
import secrets
token_hex = secrets.token_hex(16)
print(token_hex)
# 输出字符串 需要转为字节: 71cd0749b21cd3af7cc73fe965c00c30
print(token_hex.encode('utf-8'))
# 转为字节: b'221a4a7e7fbe6a024cf037dbd4214039'
token_urlsafe = secrets.token_urlsafe()
print(token_urlsafe)
# 输出字符串 需要转为字节: 0LXrsyB7xQsoBkc3D8NrQrj5Jyc4B8eYkM0ueULYFP8
print(token_urlsafe.encode('utf-8'))
# 转为字节: b'0a8ijqWZgpiEglrFzihPlO53spDPevwgeqH4oMIQLao'
token_bytes = secrets.token_bytes()
print(token_bytes)
# 输出字节: b'J\x10\xdeE4 W/\xef\x0b\x895\xf7\x9a\xd7Uc\x83\xb9\xf0\x83"\x01\x91\x064L>\x84\x96\xe9T'
3. 查看所有hash方法
import hashlib
print(hashlib.__all__)
'''
(
'md5',
'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
'blake2b', 'blake2s',
'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
'shake_128', 'shake_256',
'new',
'algorithms_guaranteed', 'algorithms_available',
'pbkdf2_hmac'
)
'''
4. 以MD5为例 (除特殊方法) 其余方法直接套用
4.1
import hashlib
pwd = b'123456'
hash_md51 = hashlib.md5(pwd)
hash_value = hash_md51.hexdigest()
print(hash_value) # e10adc3949ba59abbe56e057f20f883e
4.2
import hashlib
hash_md51 = hashlib.md5(b'123456')
hash_value = hash_md51.hexdigest()
print(hash_value) # e10adc3949ba59abbe56e057f20f883e
4.3
import hashlib
hash_md5 = hashlib.new('md5', b'')
hash_md5.update(b'123456')
v = hash_md5.hexdigest()
print(v) # e10adc3949ba59abbe56e057f20f883e
4.3.1 加盐 (salt)
import hashlib
hash_md5 = hashlib.new('md5', b'123123')
hash_md5.update(b'123456')
v = hash_md5.hexdigest()
print(v) # 207fa18ec8125a9bfeb825012e5ea911
4.4
import hashlib
def MD5(s1, s2):
obj = hashlib.md5(str(s1).encode('utf-8'))
obj.update(str(s2).encode('utf-8'))
return obj.hexdigest()
print(MD5('', '123456')) # e10adc3949ba59abbe56e057f20f883e
4.4.1 加盐 (salt)
import hashlib
def MD5(salt, s2):
obj = hashlib.md5(str(salt).encode('utf-8'))
obj.update(str(s2).encode('utf-8'))
return obj.hexdigest()
print(MD5('123123', '123456')) # 207fa18ec8125a9bfeb825012e5ea911
5. new
作用:通用的哈希对象创建函数,可以指定算法名称。
import hashlib
# s = b'hello'
# hash_obj = hashlib.new('sha256', s)
# hash_obj.update(b'123123')
# print(hash_obj.hexdigest()) # 输出: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
s = b'hello'
hash_obj = hashlib.new('md5', s)
hash_obj.update(b'123123')
print(hash_obj.hexdigest()) # 输出: '1e350c24c64c6c64d2fcad12ba3e3572'
hashlib.new()
可以指定的算法取决于 Python 环境中可用的哈希算法。可以通过 hashlib.algorithms_guaranteed
和 hashlib.algorithms_available
查看支持的算法。
通常,hashlib.new()
可以指定以下算法:
-
MD5:
'md5'
-
SHA-1:
'sha1'
-
SHA-224:
'sha224'
-
SHA-256:
'sha256'
-
SHA-384:
'sha384'
-
SHA-512:
'sha512'
-
BLAKE2s:
'blake2s'
-
BLAKE2b:
'blake2b'
-
SHA3-224:
'sha3_224'
-
SHA3-256:
'sha3_256'
-
SHA3-384:
'sha3_384'
-
SHA3-512:
'sha3_512'
-
SHAKE-128:
'shake_128'
-
SHAKE-256:
'shake_256'
6. 如果你安装了第三方库或在特定平台上运行,hashlib.new()
可能还支持其他算法。可以通过以下代码检查当前 Python 环境中可用的算法:
import hashlib
print(hashlib.algorithms_guaranteed) # 查看保证可用的算法
print(hashlib.algorithms_available) # 查看所有可用的算法
"""
{'shake_256', 'sha3_512', 'sha3_224', 'sha3_256', 'blake2b', 'sha256', 'sha224', 'shake_128', 'sha1', 'blake2s', 'sha384', 'sha512', 'sha3_384', 'md5'}
{'shake_256', 'sha3_512', 'sha3_256', 'sha224', 'blake2s', 'sha512', 'sha1', 'sm3', 'ripemd160', 'md5-sha1', 'sha384', 'blake2b', 'sha3_384', 'md5', 'sha512_224', 'sha3_224', 'sha512_256', 'sha256', 'shake_128'}
"""
7. pbkdf2_hmac
- 作用:使用 PBKDF2(基于 HMAC 的密钥派生函数)从密码生成密钥。
- 用法:
key = hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)
import hashlib
password = b'password'
salt = b'salt'
key = hashlib.pbkdf2_hmac('sha256', password, salt, 100000)
print(key.hex()) # 输出: 密钥的十六进制字符串
hashlib.pbkdf2_hmac
:这是 Python 标准库提供的 PBKDF2 实现。
-
'sha256'
:指定使用的哈希函数,可以换成'sha512'
等其他函数。 -
password.encode('utf-8')
:将密码转换为字节格式。 -
salt
:使用生成的随机盐值。 -
iterations
:迭代次数,越高越安全,默认推荐至少 100,000 次。 -
dklen
:派生密钥的长度,这里为 32 字节(256 位)。
hashlib.pbkdf2_hmac
函数支持多种哈希算法,但不是所有你列出的算法都能用于 pbkdf2_hmac
。下面我将详细解释哪些算法可以用于 hashlib.pbkdf2_hmac
,以及其他列出的属性的用途。
7.1 支持的哈希算法
在 hashlib.pbkdf2_hmac
中,支持的哈希算法主要包括以下几种:
md5
:消息摘要算法 5,产生 128 位(16 字节)哈希值,不推荐用于密码哈希,因为它安全性较低。sha1
:安全哈希算法 1,产生 160 位(20 字节)哈希值,也不再推荐用于安全应用。sha224
:SHA-2 家族中的一个成员,产生 224 位(28 字节)哈希值。sha256
:SHA-2 家族中的一个成员,产生 256 位(32 字节)哈希值,推荐用于大多数安全需求。sha384
:SHA-2 家族中的一个成员,产生 384 位(48 字节)哈希值。sha512
:SHA-2 家族中的一个成员,产生 512 位(64 字节)哈希值,推荐用于更高安全需求。
此外,hashlib
支持其他算法,但不适用于 pbkdf2_hmac
:
7.2 不适用于 pbkdf2_hmac
的算法
-
blake2b
和blake2s
:- 这是高效的哈希函数,通常用于替代 MD5 和 SHA-1,但不能用于
pbkdf2_hmac
。
- 这是高效的哈希函数,通常用于替代 MD5 和 SHA-1,但不能用于
-
sha3_224
、sha3_256
、sha3_384
、sha3_512
:- 这是 SHA-3 系列哈希算法,不直接支持
pbkdf2_hmac
。
- 这是 SHA-3 系列哈希算法,不直接支持
-
shake_128
和shake_256
:- 这是可变长度的 SHA-3 系列算法,同样不支持在
pbkdf2_hmac
中使用。
- 这是可变长度的 SHA-3 系列算法,同样不支持在
7.3 小结
在 hashlib.pbkdf2_hmac
中,你只能使用以下哈希算法:
'md5'
'sha1'
'sha224'
'sha256'
'sha384'
'sha512'
其他列出的算法和属性要么不适用于 pbkdf2_hmac
,要么与密码哈希无关。建议使用 sha256
或更强的算法用于实际密码哈希,避免使用 md5
和 sha1
,因为它们已被认为不再安全。
8. scrypt
是一种强大的密码哈希函数,设计时专注于抵抗大规模硬件攻击(如 FPGA 和 ASIC 攻击)。它与 bcrypt
和 PBKDF2
类似,但提供了更好的安全性,因为它需要大量的内存来执行哈希计算,从而增加了攻击者的成本。
scrypt
的特点
- 内存硬性要求:
scrypt
设计为对内存使用敏感,增加了大规模并行硬件破解的难度。 - 可调参数:允许通过调整参数来控制计算的复杂度和内存需求。
- 内置盐值:与
bcrypt
一样,scrypt
也自动生成并使用盐值,确保即使相同的密码每次生成的哈希也不同。
8.1 使用 scrypt
进行密码哈希和验证
以下是使用 Python 的 hashlib
库内置的 scrypt
函数来进行密码哈希和验证的基本步骤:
import os
import hashlib
def hash_password(password, salt=None, n=2**14, r=8, p=1):
if not salt:
salt = os.urandom(16) # 生成16字节的随机盐
key = hashlib.scrypt(password.encode('utf-8'), salt=salt, n=n, r=r, p=p, maxmem=0, dklen=64)
return salt + key # 返回盐和哈希值的组合
hashed = hash_password('mypassword')
print(hashed) # 输出二进制数据(盐 + 哈希)
def check_password(stored_password, provided_password, n=2**14, r=8, p=1):
salt = stored_password[:16] # 提取前16字节的盐
stored_key = stored_password[16:] # 提取哈希值
key = hashlib.scrypt(provided_password.encode('utf-8'), salt=salt, n=n, r=r, p=p, maxmem=0, dklen=64)
return key == stored_key # 比较生成的哈希值与存储的哈希值 # 验证密码
result = check_password(hashed, 'mypassword')
print(result) # 输出: True 或 False
8.2 示例代码
完整的密码哈希和验证示例:
import os
import hashlib
# 生成哈希
def hash_password(password, salt=None, n=2**14, r=8, p=1):
if not salt:
salt = os.urandom(16) # 生成16字节的随机盐
key = hashlib.scrypt(password.encode('utf-8'), salt=salt, n=n, r=r, p=p, maxmem=0, dklen=64)
return salt + key # 返回盐和哈希值的组合
# 验证密码
def check_password(stored_password, provided_password, n=2**14, r=8, p=1):
salt = stored_password[:16] # 提取前16字节的盐
stored_key = stored_password[16:] # 提取哈希值
key = hashlib.scrypt(provided_password.encode('utf-8'), salt=salt, n=n, r=r, p=p, maxmem=0, dklen=64)
return key == stored_key # 比较生成的哈希值与存储的哈希值
# 使用示例
if __name__ == "__main__":
# 用户注册时生成哈希
password = "mypassword"
hashed = hash_password(password)
print(f"Hashed password: {hashed}")
# 用户登录时验证密码
result = check_password(hashed, "mypassword")
print(f"Password is correct: {result}") # 输出: True
8.3 调整参数
scrypt
的参数可以根据需要调整:
n
:工作因子,必须是2的幂,值越大,计算成本越高,安全性越强。推荐值在2**14
到2**20
之间。r
:块大小,推荐值为8
。p
:并行因子,推荐值为1
。增加此值会增加 CPU 成本。
8.4 安全性注意事项
scrypt
在默认参数下已经非常安全,可以抵抗大规模硬件攻击。通过增加n
值可以进一步提高安全性,但会增加计算成本。- 与
bcrypt
相比,scrypt
对内存的要求更高,适用于需要极高安全性的应用场景。
scrypt
是现代密码哈希的推荐选择之一,特别适合在需要高安全性、需要防止大规模硬件破解的场景中使用。
9. bcrypt
是一种密码哈希函数,专为密码存储而设计,具有以下特点:
- 慢速计算:相比于 MD5 或 SHA 系列哈希算法,
bcrypt
的计算速度更慢,这使得它在抵御暴力破解时更加有效。 - 内置盐值:
bcrypt
在生成哈希时会自动生成并包含一个随机的盐值,无需手动管理。 - 调整复杂度:可以通过调整计算成本(cost factor)来增加计算的复杂度,从而增加破解难度。
9. 1使用 bcrypt
进行密码哈希和验证
下面是使用 Python 的 bcrypt
库进行密码哈希和验证的基本步骤:
pip install bcrypt
# # 生成随机盐 salt
# bcrypt.gensalt()
# # 加密
# bcrypt.hashpw()
# # 密码比较
# bcrypt.checkpw()
# # 派生密钥
# bcrypt.kdf()
import bcrypt
# 原始密码
password = b"111"
# 生成随机盐值
salt = bcrypt.gensalt()
print(salt)
# 派生密钥
derived_key = bcrypt.kdf(
password=password,
salt=salt,
desired_key_bytes=32, # 期望生成的密钥长度
rounds=12, # 工作因子(迭代次数)
)
print(f"Derived key: {derived_key.hex()}")
-
生成密码哈希:
import bcrypt
def hash_password(password):
# 生成盐值并创建哈希
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed_password
hashed = hash_password('mypassword')
print(hashed) # 输出: b'$2b$12$...'
bcrypt.gensalt()
:生成一个包含计算成本(默认值为 12)的盐值。bcrypt.hashpw()
:生成密码的哈希值。返回的哈希值中包含盐值和成本因子。
-
验证密码:
def check_password(provided_password, stored_hash):
return bcrypt.checkpw(provided_password.encode('utf-8'), stored_hash)
# 验证密码
result = check_password('mypassword', hashed)
print(result) # 输出: True 或 False
bcrypt.checkpw()
:验证用户提供的密码与存储的哈希值是否匹配。
9.2 示例代码
完整的密码哈希和验证示例:
import bcrypt
# 生成哈希
def hash_password(password):
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed_password
# 验证密码
def check_password(provided_password, stored_hash):
return bcrypt.checkpw(provided_password.encode('utf-8'), stored_hash)
# 使用示例
if __name__ == "__main__":
# 用户注册时生成哈希
password = "mypassword"
hashed = hash_password(password)
print(f"Hashed password: {hashed}")
# 用户登录时验证密码
result = check_password("mypassword", hashed)
print(f"Password is correct: {result}") # 输出: True
9.3 调整成本因子
bcrypt
的成本因子(cost factor)控制了哈希计算的复杂度,默认为 12。你可以调整这个值来增加或减少计算时间:
salt = bcrypt.gensalt(rounds=14) # 增加成本因子到 14,增加计算时间
9.4 安全性注意事项
- 避免硬编码成本因子:成本因子越高,哈希计算越慢,但同时也更安全。可以根据服务器性能适当调整。
- 不可逆:一旦密码被哈希化,就无法从哈希值中还原原始密码。
bcrypt
被广泛认为是安全的密码哈希算法,适合用于存储用户密码等需要高安全性的场景。