Bcrypt 哈希
Bcrypt 哈希是生成和校验 bcrypt 密码哈希的免费在线工具:输入密码、选工作因子 cost 与版本前缀($2b$、$2a$、$2y$),即可得到 60 位哈希;粘贴已有哈希可校验密码是否匹配,并拆解出版本、cost、盐与哈希四段。bcrypt 只用密码的前 72 个字节,超出部分会提前标出。全部计算在浏览器本地完成,密码不上传。
生成 bcrypt 哈希
已用 0 / 72 字节
2^10 = 1,024 轮;按本机校准估算,生成一次约 —
bcrypt 不接受空密码,输入密码后才能生成。
密码与哈希都在本机计算,不上传、不保存。
校验 / 拆解 bcrypt 哈希
已用 0 / 72 字节
密码与哈希都在本机计算,不上传、不保存(只记住 cost、前缀与是否遮挡这三项偏好)。
如何使用Bcrypt 哈希
bcrypt 怎么生成:在「生成 bcrypt 哈希」卡里输入密码,选工作因子 cost(默认 10,推荐 12)和版本前缀(Python、Node 用 $2b$,PHP、Laravel、htpasswd 用 $2y$,老版本库用 $2a$),点「生成 bcrypt 哈希」。每次都会换一个新的随机盐,所以同一个密码每次结果都不同,这是正常的。
结果下面会把哈希拆成版本、cost、盐、哈希四段,并同时给出另外两种前缀的写法,都能直接复制;点「拿去校验」会把密码和哈希一起带到下面的校验卡,立刻显示匹配。密码框上方实时显示「已用多少 / 72 字节」,超出部分不参与哈希,本页会标出从哪个字开始被忽略。
bcrypt 怎么校验:把已有的哈希粘进「校验 / 拆解」卡(可以带 Spring 的 {bcrypt} 前缀、htpasswd 的「用户名:」或引号,本页会自动去掉),再输入密码,cost 不超过 12 时会自动给出匹配或不匹配。不匹配时点「帮我排查」,会试去首尾空格、大小写反转、全角转半角这几种常见手误,命中就能一键套用。
关于Bcrypt 哈希的常见问题
- bcrypt 在线生成会上传我的密码吗?
- 不会。本页的 bcrypt 生成与校验全部在你的浏览器里完成:计算跑在一个独立的 Web Worker 里,密码、哈希和结果都不会发到任何服务器,打开 F12 的网络面板再算一次就能自证,断网也照样能用(页面本身会加载访问统计脚本,它不读取你填的内容)。本页只把 cost、版本前缀和是否遮挡密码这三项偏好记在本机 localStorage,密码与哈希不落盘、不进网址。输入框都关掉了拼写检查与自动填充——Chrome 的增强型拼写检查会把输入内容发到云端,对密码来说这是真实的泄露面。即便如此,网页生成的哈希适合测试数据和初始化账号,不要把真实用户的生产密码粘进任何网页,包括本页。
- bcrypt 能解密吗,怎么从哈希还原密码?
- 不能解密。bcrypt 是单向的密码哈希函数,不是加密:哈希里只有版本、cost、盐和一段摘要,不含密码本身,也没有任何密钥可以把它还原。唯一的办法是拿候选密码按同样的盐和 cost 重新算一遍,看结果是否相同——本页的校验功能做的就是这件事,网站登录时服务器做的也是这件事。正因为只能一个个猜,bcrypt 才故意设计得很慢:cost 每加 1,猜一次的成本就翻一倍。所以「bcrypt 加密」只是流行说法,本页也不提供任何解密或反查入口;需要可逆加密请用文本加密工具。
- 为什么同一个密码每次生成的 bcrypt 都不一样?
- 因为每次生成都会随机取 16 个字节作为盐,盐不同,哈希就完全不同。盐并不需要另外保存——它就写在哈希里:60 位的 bcrypt 哈希中,第 8 到第 29 位这 22 个字符就是盐。校验时程序先从哈希里读出版本、cost 和盐,再用你输入的密码按同样的参数重算,比对最后 31 位即可,所以每次生成的结果虽然不同,却都能校验通过。这正是 bcrypt 防彩虹表的原理:两个用户即使密码相同,库里存的哈希也不一样。本页的「高级:指定盐」只用于复现与测试,生产环境每条密码都必须用随机盐。
- bcrypt 的 $2a$、$2b$、$2y$ 有什么区别?
- 三者的区别来自不同实现修 bug 的历史:$2a$ 是 1999 年的原版前缀;2011 年 PHP 使用的 crypt_blowfish 发现了 8 位字符符号扩展的 bug,修正后的正确实现标为 $2y$,按旧 bug 算的老哈希标为 $2x$;2014 年 OpenBSD 修正了密码长度计数器在 255 字节以上回绕的问题,改用 $2b$。在密码不超过 72 字节、按正确实现计算的前提下,$2a$、$2b$、$2y$ 的计算过程完全相同,只差前 4 个字符。所以 PHP 输出 $2y$、Python 与 Node 输出 $2b$、Spring 输出 $2a$,彼此可以互认;遇到只认某个前缀的老工具,直接改前缀即可,本页每次生成都同时给出三种写法。
- bcrypt 的 cost 工作因子设多少合适?
- cost 表示 2 的多少次方轮密钥扩展,每加 1 计算时间翻倍。常见默认值是 10(Spring Security 的 BCryptPasswordEncoder、Go 的 bcrypt.DefaultCost 都是 10),PHP 8.4 起 password_hash 的默认值从 10 提到了 12,Laravel 新版本默认也是 12,Python 的 bcrypt.gensalt() 默认 12。OWASP 密码存储备忘单对 bcrypt 的建议是 cost 至少 10。实际取值建议按「在你的生产服务器上算一次大约 100 到 300 毫秒」反推:太低防不住离线猜测,太高会让登录接口变慢、还更容易被刷接口拖垮。本页在 cost 选择框下显示的是按你这台设备实测校准的估算耗时,服务器的数字请在服务器上实测。
- bcrypt 密码超过 72 个字节会怎样?
- bcrypt 只用密码的前 72 个字节,多出来的部分不参与计算。注意是字节而不是字符:UTF-8 下一个汉字占 3 个字节,24 个汉字就到顶了,一个 emoji 占 4 个字节。实测用本页的固定盐,25 个「鹿」能通过 24 个「鹿」的哈希校验——多打一个字也能登录。各实现处理超长密码的方式不同:PHP、Node 的 bcrypt、htpasswd 静默截断;Go 的 x/crypto/bcrypt 直接返回错误,新版 pyca bcrypt 也改为报错;hash-wasm 等 WASM 实现同样报错。本页按主流的截断口径处理,并实时显示字节用量和被忽略的部分。确实需要支持长密码时,常见做法是先用 HMAC-SHA256 处理密码、再把结果做 Base64 后交给 bcrypt;不要直接传原始二进制摘要,里面的 0 字节会让 bcrypt 提前截断。
- bcrypt 校验密码总是不匹配怎么办?
- 先点本页的「帮我排查」,它会依次试去掉首尾空格、大小写全部反转(Caps Lock)、全角转半角这几种最常见的手误,命中就告诉你差在哪,并能一键套用。都不是的话,按这个顺序核对:一是哈希是不是这个用户的,复制错行很常见;二是服务端在 bcrypt 之前有没有先处理密码,比如先做 SHA-256、拼接了 pepper、统一转小写;三是密码超过 72 字节时两边的截断或预哈希策略是否一致;四是哈希有没有被数据库字段截短,bcrypt 应为 60 位,字段至少 CHAR(60);五是存进去的密码末尾是否带了换行,命令行里用 echo 而不是 echo -n 生成时最常见。另外,哈希第 22 位盐字符只能是 . O e u 四种之一,不是的话说明哈希被改过,本页会标黄提示。
- bcrypt 和 MD5、SHA256 存密码有什么区别?
- MD5 和 SHA-256 是为速度设计的通用哈希,一块显卡每秒能算上百亿次 MD5,用它们存密码,泄露后弱密码几乎瞬间就会被猜出来;而且它们本身不带盐,相同密码得到相同哈希,可以直接查彩虹表。bcrypt 是专门的密码哈希:自带 16 字节随机盐,有可调的工作因子,cost 10 时算一次要几十到上百毫秒,猜测速度比 MD5 慢上百万倍,还可以随硬件变快逐年调高 cost。所以 MD5、SHA-256 适合校验文件完整性和做数据指纹,不适合直接存密码;存密码请用 bcrypt、scrypt 或 Argon2 这类慢哈希。
- bcrypt、Argon2、scrypt 存密码该选哪个?
- 新系统优先 Argon2id:它是 2015 年密码哈希竞赛的优胜者,同时消耗大量内存和时间,对显卡和专用硬件的抗性最好,OWASP 密码存储备忘单把它列为首选。Argon2id 不可用时可选 scrypt,同样是内存困难的算法。bcrypt 诞生于 1999 年,只消耗很少的内存,抗硬件加速能力弱于前两者,并且有 72 字节的长度上限;但它久经考验、几乎所有语言和框架都内置支持,OWASP 仍认为在老系统里使用 bcrypt(cost 至少 10)是可以接受的。已经在用 bcrypt 的系统不必急着迁移,适时调高 cost 即可。
- PHP 的 bcrypt 哈希能在 Python 里校验吗?
- 能。PHP password_hash 输出的 $2y$ 哈希,与 Python、Node 输出的 $2b$ 哈希算法相同,只是前缀不同;pyca bcrypt、Node 的 bcrypt 与 bcryptjs 都直接认 $2y$,可以原样校验。反过来,PHP 的 password_verify 也认 $2a$ 和 $2b$。Java 这边,较新版本 Spring Security 的 BCryptPasswordEncoder 能认 $2a$、$2b$、$2y$ 三种,一些老的 Java 库只认 $2a$,这时把前 4 个字符改成 $2a$ 即可。实测 macOS 自带的 htpasswd(Apache 2.4.66)认 $2y$ 和 $2a$、却不认 $2b$,从 Python 迁到 Apache 时同样改前缀就能解决。本页生成结果下方会同时给出三种前缀的写法。
bcrypt 是什么:密码加盐哈希为什么要故意变慢
bcrypt 是 1999 年基于 Blowfish 密码设计的密码哈希函数:它把密码和一段随机盐反复做 2^cost 轮密钥扩展,最后得到一串形如 $2b$10$ 加 53 位字符、共 60 位的字符串。它是 PHP password_hash、Laravel Hash::make、Spring Security BCryptPasswordEncoder、Node 的 bcrypt、Apache htpasswd -B 的默认或常用算法。
网站存密码要解决的问题是:数据库哪天泄露了,攻击者也很难反推出用户的密码。所以要做三件事——不可逆(只存哈希、不存密码)、 加盐(相同密码存成不同哈希,彩虹表失效)、足够慢(让离线猜测的成本高到不划算)。普通的MD5 与 SHA256只满足第一条,而 bcrypt 三条都满足,这就是「密码加盐哈希」与普通哈希的区别。它也不是加密:加密可以用密钥还原, 例如本站的 文本加密;bcrypt 没有任何办法还原,只能校验。
| 常见叫法 | 通常指什么 | 在本页怎么做 |
|---|---|---|
| bcrypt 在线加密 / bcrypt 加密 / bcrypt online | 其实是生成 bcrypt 密码哈希,不可逆 | 「生成 bcrypt 哈希」卡 |
| bcrypt 生成器 / bcrypt generator / 密码哈希生成 | 输入密码得到 60 位哈希 | 选 cost 与前缀后点生成 |
| bcrypt 校验 / bcrypt 在线验证 / bcrypt hash 比对 | 判断密码与已有哈希是否匹配 | 「校验 / 拆解」卡 |
| bcrypt 盐 / bcrypt cost | 哈希里的第 8–29 位与第 5–6 位 | 粘贴哈希即自动拆解 |
一串 bcrypt 哈希怎么读:版本、cost、盐与哈希
以密码 deertools、固定盐(16 字节 5f2a910c7ed348a116bb02e96d34c857)、cost 10 为例,本页生成的 $2b$ 哈希是:
$2b$10$VwoPBF5RQICUsuJnZRRGTumzKaT.2GDee8QnBCcXCgfu3wyQgeAxO| 位置 | 内容 | 本例 | 含义 |
|---|---|---|---|
| 第 1–4 位 | 版本前缀 | $2b$ | 用哪个实现的口径算的,见下一节 |
| 第 5–7 位 | cost | 10$ | 两位数字加分隔符,10 表示 2^10 = 1024 轮 |
| 第 8–29 位 | 盐(22 个字符) | VwoPBF5RQICUsuJnZRRGTu | 16 个随机字节的 bcrypt Base64 写法 |
| 第 30–60 位 | 哈希(31 个字符) | mzKaT.2GDee8QnBCcXCgfu3wyQgeAxO | 23 个字节的计算结果 |
bcrypt 用的 Base64 和标准 Base64 不一样:字母表是 ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789(点号是 0、斜杠是 1),没有加号, 也没有等号补齐,所以不能直接拿 Base64 解码 工具解出盐。22 个字符共 132 位, 而盐只有 128 位,所以第 22 个字符只有高 2 位有效,规范取值只有 . O e u 四种——看到这一位是别的字符, 说明哈希被手改过或复制错了,本页拆解时会标黄提示。
$2a$ $2b$ $2y$ 区别:bcrypt 版本前缀对照
常说的「2a 2b 2y区别」,归根到底是不同实现修 bug 的历史。密码不超过 72 字节、按正确实现计算时,$2a$、$2b$、$2y$ 的计算完全相同, 同一个哈希只改前缀就能互认:
| 前缀 | 来历 | 谁在用 | 本页能否生成 |
|---|---|---|---|
| $2b$ | 2014 年 OpenBSD 修正密码长度计数器在 255 字节以上回绕的问题后启用 | 当前 OpenBSD 与 pyca bcrypt、Node bcrypt 的默认输出 | 能,三种写法同时给出 |
| $2a$ | 1999 年原版前缀,后补充规定密码按 UTF-8 编码并计入结尾空字符 | Spring Security、jBCrypt 等 Java 库与不少老系统的默认输出 | 能,三种写法同时给出 |
| $2y$ | 2011 年 crypt_blowfish 修正 8 位字符符号扩展 bug 后,用来标记「正确实现」 | PHP password_hash、Laravel Hash::make、Apache htpasswd -B 的输出 | 能,三种写法同时给出 |
| $2x$ | 同在 2011 年由 crypt_blowfish 引入,专门标记按旧 bug 算出的老哈希 | 只为兼容旧数据存在;密码含中文等非 ASCII 字符时与正确算法结果不同 | 只校验(密码含非 ASCII 字符时不校验) |
一个实测例子:macOS 26.5 自带的 htpasswd(Apache 2.4.66)校验同一串哈希,$2y$ 和 $2a$ 都通过,改成 $2b$ 就报失败。 从 Python 或 Node 迁到 Apache basic auth 时遇到「密码明明对却登录不上」,把前缀改成 $2y$ 就能解决。
bcrypt 工作因子 cost:每加 1 耗时翻倍
bcrypt cost 也叫 bcrypt工作因子、rounds 或 strength,取值 4 到 31,表示 2 的多少次方轮密钥扩展。它和密码长度无关, 只决定一次计算有多慢:cost 每加 1,生成和校验都慢一倍,离线猜测的成本也翻一倍(用 密码强度检测 可以看到慢哈希对破解时间的影响)。下表是方案期在 Node 24 + hash-wasm、 Apple Silicon 笔记本上的实测,仅作量级参考;本页在 cost 选择框下显示的是对你这台设备现场校准后的估算:
| cost | 轮数 | 单次耗时 | 适用 |
|---|---|---|---|
| 4 | 16 | 约 1 ms | 最低值,只用于单元测试 |
| 8 | 256 | 约 23 ms | 测试数据 |
| 10 | 1,024 | 约 90 ms | 多数库的默认值,OWASP 建议的下限 |
| 12 | 4,096 | 约 360 ms | PHP 8.4、Laravel、pyca bcrypt 的默认值 |
| 13 | 8,192 | 约 720 ms | 服务器较强时可用 |
| 14 | 16,384 | 约 1.4 秒 | 登录明显变慢,需评估 |
手机浏览器通常比上表慢 2 到 4 倍。本页生成上限是 cost 15;校验时 cost 13 及以上改为手动点按钮并先显示预计耗时, cost 16 以上还要再确认一次,计算过程随时可以取消。
bcrypt 72字节限制:中文密码 24 个字就到顶
Blowfish 的 P 数组是 18 个 32 位子密钥,正好 72 字节,bcrypt 只把密码循环填进这 72 个字节,所以多出来的部分不参与计算。 按 UTF-8 编码,英文字母和数字 1 个字节,汉字 3 个字节,emoji 4 个字节。几个实测例子(固定盐,可在本页「填入示例」里复现):
| 密码 | 字节数 | 结果 |
|---|---|---|
| 24 个「鹿」 | 72 | 恰好用满,不截断 |
| 25 个「鹿」 | 75 | 最后一个字被忽略,能通过上一行的哈希校验 |
| 1 个 a + 24 个「鹿」 | 73 | 截断点落在最后一个「鹿」中间,它只有前 2 个字节参与 |
| 80 个 a | 80 | 与 72 个 a 的哈希完全相同 |
| 18 个 😀 | 72 | 恰好用满;第 19 个起被忽略 |
24 个「鹿」在 cost 5 下的哈希是 $2y$05$..CA.uOD/eaGAOmJB.yMBuxtHFYMDOVqaBUPjO61rJ7m/UNI5R1pm,用 25 个「鹿」校验它同样匹配。 不同实现对超长密码的处理不一样:PHP、Node 的 bcrypt、htpasswd 静默截断,Go 的 x/crypto/bcrypt 直接报错,新版 pyca bcrypt 也改为报错, hash-wasm 这类 WASM 实现同样报错。跨语言迁移时这是一个隐蔽的坑。确实要支持长密码,可以先对密码做 HMAC-SHA256 (用服务端的密钥,即 pepper),把结果按 Base64 编码后再交给 bcrypt;不要直接传原始二进制摘要,其中的 0 字节会让 bcrypt 提前截断。
PHP password_hash、Laravel、Spring Security bcrypt 与 htpasswd bcrypt 的写法
下面这些写法生成的哈希都能在本页校验,本页生成的哈希也都能被它们校验:
// PHP password_hash:PASSWORD_DEFAULT 目前就是 bcrypt,输出 $2y$
$hash = password_hash('deertools', PASSWORD_BCRYPT, ['cost' => 10]);
password_verify('deertools', $hash); // true
// Laravel 密码哈希:Hash::make / Hash::check 底层同样是 bcrypt
$hash = Hash::make('deertools');// Spring Security BCrypt:BCryptPasswordEncoder 默认 strength 10,输出 $2a$
PasswordEncoder enc = new BCryptPasswordEncoder(10);
String hash = enc.encode("deertools");
enc.matches("deertools", hash); // true
// DelegatingPasswordEncoder 存库时会在前面加 {bcrypt},本页校验时会自动去掉# Python(pyca bcrypt),输出 $2b$
import bcrypt
h = bcrypt.hashpw("deertools".encode("utf-8"), bcrypt.gensalt(rounds=10))
bcrypt.checkpw("deertools".encode("utf-8"), h) # True
// Node(bcrypt / bcryptjs),输出 $2b$
const hash = await bcrypt.hash("deertools", 10);
await bcrypt.compare("deertools", hash); // true
// Go(golang.org/x/crypto/bcrypt),输出 $2a$
h, err := bcrypt.GenerateFromPassword([]byte("deertools"), 10)# htpasswd bcrypt:-B 用 bcrypt,-C 指定 cost,-n 只打印不写文件
htpasswd -nbBC 10 admin deertools
# 输出形如 admin:$2y$10$…,整行粘进本页的哈希框即可校验几个细节:Laravel密码哈希走的就是 PHP 的 password_hash,所以输出 $2y$;Spring 的 DelegatingPasswordEncoder 会在哈希前加 {bcrypt} 标明算法,校验前要去掉;htpasswd 文件每行是「用户名:哈希」,例如 u:$2y$04$shWDieNukBu/qxIFXL2eGuRomwhFqNm4AjvZntdLYTXJtQyxN6zn.(密码 deertools),整行粘进本页即可。数据库字段至少留 CHAR(60),短了会把哈希截断, 之后任何密码都校验不过。
bcrypt 校验不匹配的排查清单
| 原因 | 典型表现 | 怎么确认 |
|---|---|---|
| 首尾多了空格 | 从表格或聊天记录复制密码 | 本页提示「首尾有空白」;「帮我排查」会试去空格 |
| Caps Lock | 大小写整体反了 | 「帮我排查」会试大小写反转 |
| 全角字符 | 中文输入法处在全角模式 | 本页提示「含全角字符」;「帮我排查」会试转半角 |
| 末尾换行 | 用 echo 而不是 echo -n 生成 | 服务端打印密码字节长度核对 |
| 服务端先处理过密码 | 先 SHA-256、拼 pepper、转小写 | 查服务端代码,bcrypt 之前那一步 |
| 哈希被截短 | 数据库字段不足 60 位 | 本页会提示「应为 60 位,当前 N 位」 |
| 哈希被改过 | 盐的第 22 位不是 . O e u | 本页拆解时标黄 |
密码首尾的空格是会参与哈希的:同一个盐,deertools 与末尾多一个空格的 deertools 得到的是完全不同的两串, 后者是 $2b$10$VwoPBF5RQICUsuJnZRRGTuz.jOH2Q4J71..s5rIJeV.uBjzba7S0y。本页不会替你自动去掉密码里的空格,只会提示。
本页的边界
- 不支持空密码:OpenBSD 原版允许空口令,但本页使用的 WASM 实现要求密码至少 1 个字节,空口令哈希无法生成也无法校验。
- 生成的 cost 上限是 15;更高 cost 的哈希可以校验,但会先显示预计耗时并要求确认。
- $2x$ 前缀的哈希在密码含中文等非 ASCII 字符时不校验,因为它代表按旧 bug 算出的结果,给出的结论可能是错的。
- 不做解密、反查或字典猜测(bcrypt 不可逆);不做批量生成或校验;不做 Argon2、scrypt、PBKDF2。要算签名请用 HMAC 生成器,HMAC 是签名而不是存密码的办法。
- 不保存密码与哈希,只记住 cost、版本前缀和是否遮挡密码这三项偏好。