必须指定hash_name="sha256"或更强、iterations≥600000、salt为16字节以上密码学随机值且每次唯一;否则哈希无安全意义,因pbkdf2_hmac不校验参数安全性。

直接说结论:用 hashlib.pbkdf2_hmac 哈希密码时,必须指定 hash_name="sha256"(或更强的 "sha512"),iterations 至少 600_000,salt 必须是 16 字节以上随机字节,且每次哈希都得用新 salt —— 少一个条件,就等于白做。
为什么不能只调用 pbkdf2_hmac 就完事?
这个函数本身不校验参数安全性,它只是忠实执行 PBKDF2 算法。传 iterations=1、salt=b"123"、hash_name="md5" 它也照算,但结果毫无安全意义。
-
hash_name必须是抗碰撞性强的哈希算法:"sha256"是当前最低可用底线,"md5"和"sha1"已被明确弃用 -
iterations决定计算耗时:2024 年主流推荐 ≥ 600_000(Python 3.12+ 默认已升至 600_000);低于 100_000 在现代 CPU 上可能不到 1ms,攻击者可暴力试千万次/秒 -
salt必须唯一且不可预测:不能复用、不能是用户名、不能是固定字符串;必须用os.urandom(32)这类密码学安全随机源生成
如何生成安全的 salt 并保存哈希值?
salt 不是密钥,不需要保密,但必须和哈希值一起持久化存储(比如存在数据库同一行)。常见错误是把 salt 硬编码或全库共用一个。
正确做法是:每次注册/改密都生成新 salt,并把 salt + hash 拼成标准格式(如 sha256:600000:bytes_b64:salt_b64 或直接存为二进制字段):
import hashlib import os <p>password = b"my_secret" salt = os.urandom(32) # 32 字节,足够抵抗彩虹表 key = hashlib.pbkdf2_hmac( hash_name="sha256", password=password, salt=salt, iterations=600_000 )</p><h1>存储时:把 salt 和 key 一起保存(例如用 bytes.join 或 base64 编码)</h1><p>stored = salt + key # 验证时先取前 32 字节为 salt,后 32 字节为 hash(若用 sha256)</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill8054" title="python-parallelization"><img src="https://img.php.cn/upload/skill/000/000/081/179181013081932.jpg" alt="python-parallelization" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill8054" title="python-parallelization" class="overflowclass">python-parallelization</a> <p class="overflowclass">将顺序 Python 代码转换为并行/并发实现,用于在需要并行化 Python 代码、提升性能时使用。</p> </div> <a rel="nofollow" href="/xiazai/skill8054" title="python-parallelization" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
验证密码时怎么避免时序攻击?
别用 == 直接比较两个 bytes —— Python 的 == 是短路比较,攻击者可通过响应时间差异推断 hash 字节内容。
- 必须用
hmac.compare_digest()做恒定时间比对 - 注意:
compare_digest要求两个参数类型一致(都是bytes),不能一个是str一个是bytes - 验证流程必须完整重放哈希过程:取库存的 salt → 用同样
iterations和hash_name计算 → 再恒定时间比对
示例:
import hmac
<p>def verify_password(stored: bytes, password: bytes) -> bool:
salt = stored[:32]
expected_key = stored[32:]
actual_key = hashlib.pbkdf2_hmac("sha256", password, salt, 600_000)
return hmac.compare_digest(actual_key, expected_key)</p>
容易被忽略的兼容性细节
不同语言/框架对 PBKDF2 的默认输出长度可能不同。Python 的 pbkdf2_hmac 默认返回与所选哈希算法输出等长的 bytes(如 sha256 → 32 字节),但有些系统会显式指定 dklen 参数。
- 如果你要和 Node.js 的
crypto.pbkdf2Sync(..., 'sha256', 600000, 32)互通,就得确保dklen=32显式传入(虽然默认就是 32,但显式写更安全) - SQLite、PostgreSQL 等数据库扩展(如
pgcrypto)通常不直接支持 PBKDF2,得靠应用层处理;不要指望 SQL 函数能替代pbkdf2_hmac - Python 3.9+ 支持
usedforsecurity=True参数(默认开启),旧版本无此参数,无需额外操作
真正麻烦的从来不是调用那一行代码,而是 salt 怎么生成、怎么存、怎么读、怎么比 —— 这些环节漏掉任意一环,前面加再多 iteration 都挡不住泄露。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










