密钥派生函数:PBKDF2、Argon2和scrypt对比
比较基于密码加密的密钥派生函数。PBKDF2、Argon2和scrypt的优势和权衡。
2026 年密码派生加密密钥的推荐选择:Argon2id 排首位(密码哈希竞赛获胜者,OWASP 首选,能有效抵御 GPU 和 ASIC 攻击);scrypt 是可靠的次选(内存硬化,广泛用于加密货币领域);PBKDF2-SHA-256 在 60 万次以上迭代下仍在兼容性场景中可接受,但对 GPU 的抵抗力有限。对于允许用户设置密码来保护共享文件的传输应用,Argon2id(3 次迭代、64 MiB 内存、4 并行度)是当前的现代基准。PBKDF2 之所以仍在使用,是因为它内置于 Web Crypto API,无需 WASM 依赖。以下是三者的差异与各自的适用场景。
快速对比
| 属性 | PBKDF2 | scrypt | Argon2id | |---|---|---|---| | 引入年份 | 2000 年(RFC 2898) | 2009 年(RFC 7914) | 2015 年(PHC 获胜者) | | 内存硬化 | 否 | 是 | 是 | | 参数灵活性 | 仅迭代次数 | N、r、p | 时间、内存、并行度 | | GPU 抗性 | 弱 | 中等 | 强 | | ASIC 抗性 | 非常弱 | 中等 | 强 | | 浏览器原生(Web Crypto) | 是 | 否 | 否 | | OWASP 2024 建议 | 可接受的备选 | 可接受 | 首选 | | 典型浏览器耗时(现代硬件) | 60 万次迭代 ≈ 500 ms | N=2^17 ≈ 800 ms | 3 次迭代、64 MiB ≈ 1 s |
为什么内存硬化至关重要
密码派生加密的威胁模型是离线暴力破解。攻击者获取密文和盐值后,通过密码字典逐一尝试,看哪个推导出的密钥能解密成功。防御的核心是让每次尝试代价高昂。
PBKDF2 只在 CPU 时间上增加代价(SHA-256 迭代)。现代 GPU 每秒可执行数十亿次 SHA-256 运算;一张游戏 GPU 每天可测试 1000 万至 1 亿次 PBKDF2-SHA-256(60 万次迭代)的猜测。ASIC 攻击者的效率还要高出数个数量级。
内存硬化函数(scrypt、Argon2)要求每次尝试都占用固定量的内存。GPU 和 ASIC 受限于内存带宽,每台设备的并行度有上限。64 MiB 内存需求意味着一张 16 GB 显存的 GPU 最多只能并行运行 256 次猜测,而不是数百万次。暴力破解的经济成本因此提高 2–3 个数量级。
PBKDF2:沿用至今的遗留标准
PBKDF2(RFC 2898)在密码和盐值上迭代调用一个伪随机函数,通常是 HMAC-SHA-256 或 HMAC-SHA-512。迭代次数是唯一可调参数。
const passwordKey = await crypto.subtle.importKey(
"raw", new TextEncoder().encode(password),
"PBKDF2", false, ["deriveKey"]
);
const aesKey = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt, // 16 字节随机数
iterations: 600000,
hash: "SHA-256",
},
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
OWASP 2023 建议 PBKDF2-SHA-256 至少使用 60 万次迭代。部分旧规范(例如 LastPass 2018 年默认的 100100 次)在 2026 年已被认为过低。
优势:内置于 Web Crypto,无需 WASM,经 FIPS 验证,在 Node 和浏览器中行为一致。
局限:没有内存硬化,易受 GPU 和 ASIC 大规模并行攻击。迭代次数翻倍,攻击者和合法用户的代价都翻倍。用户忍耐度有限,迭代次数到一定程度就会遇到 UX 天花板。
scrypt:首个大规模部署的内存硬化方案
scrypt(RFC 7914)由 Colin Percival 于 2009 年为 Tarsnap 设计。它将密码材料通过一个大内存缓冲区混合处理,迫使攻击者在每次猜测时都必须持有该缓冲区。
三个参数:
- N:成本因子(通常为 2^14 至 2^20)。内存占用约为 128 × N × r 字节。
- r:块大小(通常为 8)。影响内存占用和 GHASH 迭代次数。
- p:并行度(通常为 1)。更高的值可加速合法计算,但同样加速攻击者;通常保持为 1。
OWASP 建议基准参数为 N=2^17、r=8、p=1,内存占用约 128 MiB,现代硬件运行约 800 ms。
scrypt 不在 Web Crypto API 中。在 JavaScript 里,使用 scrypt-js、@noble/hashes 或 libsodium.js。Litecoin 和 Dogecoin 以 scrypt 作为工作量证明算法,这反而激励了专用 ASIC 的开发,一定程度上削弱了它最初对 ASIC 的非对称优势。
Argon2id:2026 年的默认选择
Argon2 在 2015 年密码哈希竞赛中获胜。三个变体:Argon2d(最快,数据相关访问,存在侧信道风险)、Argon2i(数据无关访问,较慢)、Argon2id(混合模式,大多数场景推荐)。RFC 9106 于 2021 年将其标准化。
三个参数:
- t(时间):遍历内存的迭代次数,通常为 2–3。
- m(内存):以 KiB 计的内存量,通常为 65536(64 MiB)或更高。
- p(并行度):并行度,通常为 1–4。
OWASP 2024 基准:最低 t=2、m=19456(19 MiB)、p=1;更强保护为 t=3、m=65536(64 MiB)、p=4。
import { argon2id } from '@noble/hashes/argon2';
import { utf8ToBytes } from '@noble/hashes/utils';
const derivedKey = argon2id(utf8ToBytes(password), salt, {
t: 3, m: 65536, p: 4, dkLen: 32
});
也可通过 argon2-browser(WASM)使用:
import argon2 from 'argon2-browser';
const hash = await argon2.hash({
pass: password, salt,
type: argon2.ArgonType.Argon2id,
time: 3, mem: 65536, parallelism: 4, hashLen: 32
});
Argon2id 对 GPU 攻击者的抵抗力强于 scrypt,因为其内存访问模式不适合批量内存设计。针对 Argon2 的 ASIC 在研究层面已有进展,但尚未达到经济可行的攻击规模。
为你的应用选择合适的参数
校准方法:确定用户能接受的最长等待时间(通常 500 ms 至 2 秒),在最慢的目标设备上测量,将参数调整到接近该预算。
对于 HexaTransfer 风格的密码保护文件共享(上传时派生一次,下载时派生一次),1–2 秒是可接受范围。参数建议:
- PBKDF2-SHA-256:60 万至 120 万次迭代
- scrypt:N=2^17、r=8、p=1
- Argon2id:t=3、m=65536、p=4
用户输入密码后需即时等待的登录系统,UX 上限通常在 300–500 ms,参数大致减半。后台重加密等无需等待的批处理场景,可将时间预算提高到 3–5 秒。
盐值管理
三种 KDF 都需要盐值。规则如下:
- 至少 16 字节随机数
- 通过
crypto.getRandomValues()生成,绝对不用Math.random() - 每个密码独立生成(Alice 和 Bob 即使使用相同密码,盐值也应不同,从而派生出不同密钥)
- 不需要保密——随密文一起存储
有时会讨论到"pepper"(服务器端的全局密钥附加值)。对于使用哑存储后端的文件传输服务,pepper 没有价值,因为没有服务器端的秘密。对于基于账户的系统,将 pepper 单独存储于密码数据库之外,可降低数据库泄露的危害。
在不同 KDF 之间迁移
如果现有系统基于 PBKDF2,想迁移到 Argon2id:
- 在密文元数据中存储 KDF 标识符(如
"kdf": "pbkdf2-sha256-600000"或"kdf": "argon2id-3-65536-4") - 新上传使用 Argon2id
- 解密时读取 KDF 标识符,调用对应函数
- 切勿盲目升级旧密文;需要原始密码才能重新派生密钥
LastPass、1Password 和 Bitwarden 都经历过这个迁移过程。三者目前默认使用 60 万次以上迭代的 PBKDF2,较新版本提供 Argon2id 选项。即便是密码管理器,向 Argon2 的过渡也是循序渐进的——生态工具链的成熟需要时间。
bcrypt 怎么样
bcrypt(1999 年)是具备适度内存硬化的密码哈希函数。它将输入截断为 72 字节(一个众所周知的陷阱——超长密码会被静默截断)。它是 Ruby on Rails 和许多 PHP 框架的历史默认值。2026 年的新代码,优先选 Argon2id;维护现有使用 bcrypt 的系统,保持现状即可。
实际推荐
2026 年新建的文件传输服务:
- 如果可以接受 15–200 KB 的依赖:通过 @noble/hashes 或 libsodium.js 使用 Argon2id
- 如果追求零依赖和最小包体积:通过 Web Crypto 使用 PBKDF2-SHA-256(60 万次迭代)
- 如果编写加密货币钱包或继承了 scrypt 遗留代码:scrypt(N=2^17)
对于处理敏感文件的生产应用,Argon2id 值得引入 WASM 依赖。对于简单的密码保护共享(用户本就会生成随机密码的场景),PBKDF2 已经足够,因为安全性来自密码本身的熵,而非 KDF。
在 hexatransfer.com 上试试 — 免费、无需注册、最多 10 GB。