口令哈希生成与验证器

完全在你的浏览器里用 bcrypt、scrypt、Argon2id 和 PBKDF2 哈希并验证口令 —— 生成 PHC 格式字符串、用候选口令核对已有哈希、并检查其中隐藏的参数。不会上传任何数据。
仅限测试口令。真实用户口令绝不应该粘贴进任何网页。本工具完全在浏览器中运行 —— 字符串从不离开你的标签页 —— 但真实的数据泄露往往始于一次粘贴。
bcrypt cost(2^cost 轮)
bcrypt 变体 变体只改变前缀;哈希本身完全一致
scrypt N(CPU 成本)
scrypt r / p r =   p = 块大小为 128·r 字节;内存 = 128·N·r 字节(N=214、r=8 → 16 MiB)
Argon2id m(内存)
Argon2id t / p t =   p = t = 迭代次数;p = 并行度(lane 数)
PBKDF2 PRF
PBKDF2 迭代次数  OWASP 2023:HMAC-SHA-256 为 600,000(SHA-1 为 1,300,000,SHA-384/512 为 210,000)
输出长度(字节)  bcrypt 输出固定为 23 字节(31 个 bcrypt-base64 字符)
算法参数内存输出长度耗时(本浏览器)
bcryptcost 124 KiB23 字节-
scryptN=214, r=8, p=116 MiB32 字节-
Argon2idm=19 MiB,t=2,p=119 MiB32 字节-
PBKDF2HMAC-SHA-256,600,000 次迭代~032 字节-
参数速查表(OWASP 口令存储速查表,2023 年最低值)
算法最低要求备注
Argon2idm = 19 MiB(19,456 KiB),t = 2,p = 1首选;内存困难
scryptN = 214, r = 8, p = 1内存困难(最低即 16 MiB)
bcryptcost = 10只有工作因子;口令上限 72 字节
PBKDF2-HMAC-SHA256600,000 次迭代仅迭代困难 —— 对 GPU 友好,最后的选择
本页的默认值都在每一条最低要求之上。生产环境请优先选 Argon2id,不可用时选 scrypt 或 bcrypt,只有出于 FIPS 合规原因才选 PBKDF2。
PHP 互操作 —— password_hash($pw, PASSWORD_BCRYPT) 输出 $2y$10$...:把它和候选口令一起粘贴到“验证”里,本页给出的答案与 password_verify() 完全一致。PASSWORD_ARGON2ID 的输出($argon2id$v=19$m=65536,t=4,p=1$...)同样可以验证。passlib、django-argon2 和 crypto.scrypt 包装的 PHC 字符串也都能解析。
自检
用公开向量重新运行页面引擎:bcrypt 的 crypt_blowfish 测试套件、node/OpenSSL 生成的 scrypt 与 Argon2id 字符串、RFC 6070 的 PBKDF2 向量与公开 SHA-256 集合,外加每种算法的“生成后再验证”往返测试和负面用例(错误口令必须失败、畸形字符串必须被拒绝)。不会向任何地方发送数据。
口令哈希从零讲起
一句话:口令哈希被刻意做得很慢(而且最好还吃内存),这样攻击者偷走数据库也无法每秒尝试数十亿次猜测 —— 与 SHA-256 这类为速度而生的快速哈希目标恰好相反。
为什么不直接对口令做 SHA-256?

SHA-256 在现代 GPU 上每秒能跑数十亿次猜测。一个 8 字符口令的组合数远低于 1013 —— 几小时 GPU 时间,大规模矿架只要几分钟。口令哈希函数(KDF)用成本参数来对抗这一点:bcrypt 把核心运算重复 2cost 次,PBKDF2 把 HMAC 迭代几十万次,scrypt 和 Argon2id 还要求每次猜测占用数 MB 内存,让速度快但每核内存少的 GPU 失去优势。盐 —— 随机生成、以明文形式存放在哈希字符串里 —— 阻止了预计算的彩虹表,并迫使每一次猜测都为每个用户重新计算。

本页的四种算法
算法年份困难性签名格式注意事项
bcrypt1999CPU(2cost 次密钥准备)$2b$12$ + 22 字符盐 + 31 字符哈希口令上限 72 字节(超出部分被静默忽略);在最初的 C 实现里 NUL 字节会终止口令
PBKDF22000(RFC 2898/8018)CPU(迭代次数)$pbkdf2-sha256$i=600000$ + 盐 + 哈希(passlib 风格)无内存成本 —— GPU 和 ASIC 的最爱;只有迭代次数可调
scrypt2009(RFC 7914)CPU + 内存(128·N·r 字节)$scrypt$ln=14,r=8,p=1$ + 盐 + 哈希N 必须是 2 的幂;内存随 r 线性增长
Argon2id2015(PHC 获胜者,RFC 9106)CPU + 内存(m KiB,t 轮)$argon2id$v=19$m=19456,t=2,p=1$ + 盐 + 哈希Argon2i(数据无关)vs Argon2d(数据相关)vs Argon2id(混合)—— 除非你知道为什么不用,永远选 id
内存困难 vs 计算困难

GPU 有数千个核心,但每个核心能访问的高速内存都很少。一个每次猜测需要 19 MiB 工作集的 KDF,会把攻击者的并行猜测数限制在其 19 MiB 内存块的数目上 —— 这就是为什么 19 MiB 的 Argon2id 在实践中可以强过 cost 15 的 bcrypt,尽管后者在你的笔记本上耗时更短。PBKDF2 对此毫无办法:它唯一的旋钮是迭代次数,而专用芯片能让它线性加速。

读懂一个 PHC 字符串

modular-crypt / PHC 格式是自描述的:在 $argon2id$v=19$m=19456,t=2,p=1$ICEiIyQlJicoKSorLC0uLw$WTGpK4hf... 中,字段依次是算法、版本、参数(m = 内存 KiB,t = 迭代次数,p = lane 数)、base64 盐、base64 哈希。bcrypt 早于该标准,使用自己紧凑的形式:$2b$ 变体、12 cost,然后是用 bcrypt 自定义 base64 字母表(./A-Za-z0-9)编码的盐和哈希。本页的“验证”接受以上全部格式并展示解析出的字段 —— 随便贴一个哈希进来,看看它由什么组成。

常见错误
  • 把快速哈希用于口令。MD5/SHA-1/SHA-256 的口令列在消费级 GPU 面前每秒被猜数十亿次。文件和数据的哈希请用 Hash Generator;口令请用本页这些算法。
  • 共用一个盐。所有用户共用一个盐意味着相同口令得到相同哈希,彩虹表卷土重来。本页每个哈希都用全新的 16 字节随机盐。
  • 无视 bcrypt 的 72 字节。"a"*100 和 "a"*72 的哈希完全相同 —— 超出部分被丢弃。口令超过 72 字节时本页会在输入框旁警告。Argon2 没有这种限制。
  • 在自己的代码里用 == 比较哈希。验证必须恒时进行;页面引擎内部就是这样做的(重新计算并按字节比较),但如果你照搬这个思路,请使用恒时比较函数。
  • 在飞快的开发机上调参数。请以生产用户的中位数硬件上 ~250–500 ms 的哈希耗时为目标,而不是你的工作站。“跑基准”按钮展示的才是这台浏览器真实的体验。
  • 每次登录校验都重新哈希。先验证;只有在验证成功之后才重新哈希(比如提高 cost),并把新字符串存回去。PHP 的 password_needs_rehash() 就是为这件事准备的。
常见问题

该选哪一个?Argon2id,至少取 OWASP 最低参数。如果运行时不提供:先 scrypt,再 bcrypt(cost 取到哈希耗时 ≥250 ms)。只有认证要求时才用 PBKDF2。

为什么输出字符串里能看到随机盐?盐不是秘密 —— 它刻意以明文存在 PHC 字符串里,这样验证时只需要字符串和候选口令。它的职责是唯一性,而不是保密。

变体($2a$ / $2b$ / $2y$)有区别吗?对 ASCII 口令,三者产生完全相同的哈希;$2a$ 是 1990 年代的原始版本,$2b$ 修复了一个罕见的符号扩展 bug(2014),$2y$ 是 PHP 用来标记已含修复的记号。“验证”三者都接受。

一次哈希应该多久?服务器硬件上大约每次登录 250–500 ms。低于 100 ms 离线破解就很便宜;超过 1 s 用户会先于安全性提升而抱怨。

能验证别处生成的哈希吗?能 —— 粘贴哈希并给出候选口令即可。已知兼容的格式:PHP password_hash(bcrypt $2y$ 与 Argon2id)、Python passlib、django 的 argon2 hasher,以及 Tarsnap 的 scrypt 工具所用的 scrypt 字符串格式。

“跑基准”按钮做什么?它用页面默认参数对当前口令各做一次哈希,并报告在本浏览器中的墙钟时间 —— 用于感受成本差异,不是严格测量。