人人都会AI编程

18.5 hashlib /hmac:加密哈希、数据校验

更新时间:2026-07-12

在开发中,我们经常需要计算数据的“指纹”——例如校验文件完整性、存储密码而不保存明文、生成签名令牌。Python 标准库中的 hashlibhmac 就是专门干这件事的工具,简单可靠,无需第三方依赖。

18.5.1 hashlib —— 通用哈希算法库

hashlib 支持多种安全哈希算法,包括 MD5、SHA-1、SHA-256、SHA-512 等,统一的使用接口让我们可以轻松切换算法。

核心用法三步走

  1. 创建一个哈希对象:hashlib.sha256()
  2. update() 喂入数据(可分多次,适合处理大文件)
  3. 调用 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 配合 saltpbkdf2_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 用于验证

生产环境中更推荐 bcryptargon2 这类专门的密码哈希库,但 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 最佳实践与踩坑提醒

  1. 永远不要用普通哈希做密码存储。至少加盐并用 PBKDF2/bcrypt/Argon2,因为普通哈希计算太快,彩虹表和暴力破解几乎无成本。
  2. 选择适当的哈希算法:非安全校验可用 SHA-256(或 MD5),安全场景务必用 SHA-256 及以上,并配合 HMAC 或专用签名算法。
  3. 编码一致性:无论是 hashlib 还是 hmac,传入的必须是字节串。字符串需要先用 encode('utf-8') 转换,否则会抛 TypeError
  4. 大文件不一次性读入内存:使用流式 update,每次读 4KB~8KB 即可。
  5. 校验时防范时序攻击:比较 HMAC 值时务必使用 hmac.compare_digest(),而不是 ==。后者可能在第一个不同字节处就返回,泄露验证耗时的信息。
  6. 密钥安全:HMAC 的密钥千万不能硬编码在代码里,应从环境变量或密钥管理系统获取,且长度建议与哈希算法的块大小一致(SHA-256 使用 32 字节密钥)。

有了 hashlibhmac,Python 已经内置了实现数据校验和安全签名的全套基础工具,轻量级场景完全够用,避免了第三方依赖的混乱。