在开发中,我们经常需要计算数据的“指纹”——例如校验文件完整性、存储密码而不保存明文、生成签名令牌。Python 标准库中的 hashlib 和 hmac 就是专门干这件事的工具,简单可靠,无需第三方依赖。
18.5.1 hashlib —— 通用哈希算法库
hashlib 支持多种安全哈希算法,包括 MD5、SHA-1、SHA-256、SHA-512 等,统一的使用接口让我们可以轻松切换算法。
核心用法三步走
- 创建一个哈希对象:
hashlib.sha256() - 用
update()喂入数据(可分多次,适合处理大文件) - 调用
hexdigest()或digest()获取结果
基本示例
import hashlib
# 字符串 → 哈希(注意要编码为字节串)
data = "Hello, Python!"
hash_obj = hashlib.sha256(data.encode('utf-8'))
print(hash_obj.hexdigest())
# 输出:一个 64 位的十六进制字符串
# 分块更新(例如处理大文件)
hash_obj = hashlib.md5()
hash_obj.update(b"Hello, ")
hash_obj.update(b"Python!")
print(hash_obj.hexdigest()) # 与一次性传入结果相同
常用算法速查
| 算法 | 构造方式 | 哈希长度(字节) | 安全性 | 备注 |
|------|----------|-----------------|--------|------|
| MD5 | hashlib.md5() | 16 | 已破解,不适合安全用途 | 可用于非安全校验,如文件去重 |
| SHA-1 | hashlib.sha1() | 20 | 已被碰撞攻击,不推荐 | 旧系统兼容 |
| SHA-256 | hashlib.sha256() | 32 | 安全,广泛使用 | 推荐用于密码哈希、数字签名 |
| SHA-512 | hashlib.sha512() | 64 | 更安全但更慢 | 适合高安全要求场景 |
安全提醒:MD5 和 SHA-1 已不安全,绝对不能用于密码存储或数据完整性安全验证。存储用户密码应使用
hashlib配合salt和pbkdf2_hmac(见下文),而不是单纯哈希。
实用技巧
- 文件哈希校验:下载文件后,可对比哈希值确认文件未被篡改。
def file_sha256(filename):
sha = hashlib.sha256()
with open(filename, 'rb') as f:
while chunk := f.read(8192):
sha.update(chunk)
return sha.hexdigest()
- 密码哈希存储:使用 PBKDF2 密钥派生函数,增加破解成本。
import hashlib, os
password = "secret123".encode()
salt = os.urandom(16) # 生成随机盐
dk = hashlib.pbkdf2_hmac('sha256', password, salt, 100000)
# dk 是派生出的密钥,通常存储 salt 和 dk 用于验证
生产环境中更推荐
bcrypt或argon2这类专门的密码哈希库,但hashlib.pbkdf2_hmac作为标准库方案已经足够许多场景。
18.5.2 hmac —— 带密钥的哈希消息认证码
普通哈希只能验证数据是否被篡改,但如果攻击者同时替换了数据和哈希值,就无从发现。HMAC(Keyed-Hashing for Message Authentication)在哈希中加入一个密钥,只有持有正确密钥的人才能生成或验证正确的哈希,从而同时保证完整性和真实性。
典型场景
- API 签名:服务端和客户端共享一个 secret,客户端用 HMAC 生成请求签名,服务端验证签名,防止伪造请求。
- 会话令牌保护:对 session ID 加签,确保该值由服务器签发。
- 防篡改的 URL 参数(如密码重置链接)。
hmac 模块用法
import hmac, hashlib
secret = b'my-secret-key'
message = b'important data'
# 创建 HMAC 对象,指定哈希算法
h = hmac.new(secret, message, hashlib.sha256)
print(h.hexdigest()) # 64位十六进制签名
# 也可以使用便捷比较函数(避免时序攻击)
expected = hmac.new(secret, message, hashlib.sha256).hexdigest()
received = h.hexdigest()
if hmac.compare_digest(received, expected):
print("签名匹配,数据可信")
hmac.compare_digest(a, b)专门用于安全比较,它不会因为字符差异位置提前返回,防止时序分析攻击。无条件信任这种比较方式。
分步更新:和 hashlib 一样,hmac 也支持分步 update,适合处理流式数据。
h = hmac.new(secret, digestmod=hashlib.sha256)
h.update(b'chunk1')
h.update(b'chunk2')
signature = h.hexdigest()
18.5.3 最佳实践与踩坑提醒
- 永远不要用普通哈希做密码存储。至少加盐并用 PBKDF2/bcrypt/Argon2,因为普通哈希计算太快,彩虹表和暴力破解几乎无成本。
- 选择适当的哈希算法:非安全校验可用 SHA-256(或 MD5),安全场景务必用 SHA-256 及以上,并配合 HMAC 或专用签名算法。
- 编码一致性:无论是
hashlib还是hmac,传入的必须是字节串。字符串需要先用encode('utf-8')转换,否则会抛TypeError。 - 大文件不一次性读入内存:使用流式
update,每次读 4KB~8KB 即可。 - 校验时防范时序攻击:比较 HMAC 值时务必使用
hmac.compare_digest(),而不是==。后者可能在第一个不同字节处就返回,泄露验证耗时的信息。 - 密钥安全:HMAC 的密钥千万不能硬编码在代码里,应从环境变量或密钥管理系统获取,且长度建议与哈希算法的块大小一致(SHA-256 使用 32 字节密钥)。
有了 hashlib 和 hmac,Python 已经内置了实现数据校验和安全签名的全套基础工具,轻量级场景完全够用,避免了第三方依赖的混乱。