1次出货登录系统,我做了那个时代每个教程都告诉我要做的事情:存储 md5($password) users 表中并继续前进。它起作用了,它悄悄地是一场灾难。多年后将 auth 构建到 WordPress 插件和 Laravel 应用程序中,我对 "密码检查通过 " 和 " 之间的差距有了更健康的尊重。如果这个数据库泄露,密码是安全的。 " Bcrypt 是缩小大部分差距的工具,几乎是我接触到的每个框架中的默认值。所以我构建了一个 b加密生成器 上 Toolz.dev that hashes一个密码与成本因素你控制, 验证一个密码与现有的哈希, 并做所有的在你的浏览器, 所以任何你打字的东西都不会发送到任何地方, 这是指南它和bcrypt本身。
TL;博士: Bcrypt 将密码变成一个加盐的,故意减慢 60 个字符的哈希值,这样泄露的数据库就不是泄露的密码列表了,The Toolz b加密生成器 hash 任何一个具有可调成本系数(4 到 31)的密码,针对你粘贴的 hash 验证一个密码,并显示它解析的版本、成本和盐,它产生标准
$2b$格式为 OpenBSD、PHPpassword_hashlaravel 和 Node bcrypt 库都接受,并且它完全运行客户端。
什么是 bcrypt?
Bcrypt 是一个密码散列函数,由 Niels Provos 和 David Mazieres 设计,并在他们 1999 年的 USENIX 论文中提出 "一种未来适应的密码方案"。它建立在Blowfish块密码之上,但有一个转折:它不是加密数据,而是一遍又一遍地运行Blowfish's键设置步骤,使用密码和随机盐作为关键材料,其结果是单向哈希计算一次便宜,计算十亿次成本高昂,这正是属性密码存储的需求。
That "purpose-purpose" idea是它的核心,像SHA-256这样的通用哈希被设计成快速,因为校验和和完整性校验想要速度,密码哈希想要相反,如果攻击者窃取了你的数据库,那么他们和每个明文密码之间唯一站立的就是猜测需要多长时间,快速哈希让现代GPU每秒测试数十亿个候选者;一个bcrypt哈希,其成本上限合理,每个核心每秒几千个泄露的文件,结果截然不同。
Bcrypt 存活了二十多年,因为它把基本面做对了,而且因为它具有适应性,成本因素意味着为 1999 年硬件调优的相同算法可以在 2026 年硬件上拨号,而无需更改其他任何内容,它仍然是推荐选项 OWASP 密码存储速查表并且它是 Laravel、Spring Security 和我工作的许多其他堆栈中的开箱即用的默认值。如果您想要更深入的背景来了解为什么快速哈希值对于这项工作来说是错误的, 数据隐私在线工具 指南涵盖了推动整个行业走向缓慢、咸化散列的推理。
bcrypt 哈希包含哪些内容?
Bcrypt 最好的地方之一就是它的输出是自描述, 取一个真实的哈希:
$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
从左到右阅读,在美元符号上拆分,它会告诉您验证密码所需的一切:
| 场 | 例子 | 意义 |
|---|---|---|
| 版本 | 2b |
bcrypt 变体。 2b 是当前更正的 OpenBSD 格式 |
| 成本 | 12 |
Work因子。键-设置运行2^12 = 4096次迭代 |
| 盐 | R9h/cIPz0gi.URNNX3kh2O |
22 个自定义 Base64 字符,解码为 16 个随机字节 |
| 消化 | PST9/PgBkqquzi.Ss7KIUgO2t0jWMUW |
31 个字符编码 23 字节的 bcrypt 输出 |
因为盐和成本住在字符串内,所以验证不需要其他任何东西。当你将哈希粘贴到 b加密生成器 verify 模式,它会直接从字符串中读取成本和盐,用那个精确的盐重新计算摘要,并进行比较,没有单独的盐列可以管理,这是 bcrypt 存储起来如此愉快的原因之一:它是用户表中的单个不透明字段。
Base64 字母表的自定义值一个注释,因为它会使人绊倒。Bcrypt 使用 ./A-Za-z0-9的,这是与标准Base64不同的排序,bcrypt盐不是标准Base64字符串,所以不要尝试用通用的Base64工具对其进行解码,并期望有意义的字节,如果你对其他地方使用的标准变体感到好奇,the base64 编码 导游会浏览普通字母表及其显示位置。
如何生成 bcrypt 哈希?
在工具中,它分为三个步骤:键入密码、选择成本、单击生成。在引擎盖下,生成哈希值比看起来要多。首先使用 Web Crypto API 创建 16 字节的加密强随机盐。然后 bcrypt 运行其昂贵的密钥时间表,用密码和盐播种 Blowfish 并迭代 2^成本倍。最后,它以生成的密码状态对固定魔弦 ("OrpheanBeholderScryDoubt") 进行 64 次加密,并对输出进行 Base64 编码。整个舞蹈产生了 60 个字符的哈希值。
最常见的惊喜是,将相同的密码哈希两次会给出两个不同的哈希值,这是正确的,也是有意的。每个哈希值都有自己的随机盐,因此两个用户都选择 hunter2 zui终存储值完全不同,盗表的攻击者不能分辨两个账号共享一个密码,不能跨账号重复使用预先计算的表,如果要重现特定的哈希进行测试,就提供全盐串而不是裸成本,这正是验证模式内部所做的。
Application 代码中你从来没有用手组装过这些, 你把库叫做:
// PHP
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Node with bcrypt / bcryptjs
const hash = await bcrypt.hash(password, 12)
// Laravel
use Illuminate\Support\Facades\Hash;
$hash = Hash::make($password); // bcrypt by default
Toolz 工具适用于该代码周围的时刻:以给定成本理智地检查哈希值是什么样子,为播种机或测试夹具生成已知的哈希值,或者确认您在配置中找到的哈希值与您认为的密码匹配它做到了。
如何选择成本因素?
成本因素是您必须考虑的一个旋钮,诚实的答案是"测量,不要复制。" OWASP 的经验法则是,在您的生产硬件上,单个哈希值应该需要大约四分之一秒的时间。该目标平衡了两种相反的力量:太低,您使破解变得便宜、太高,并且每个合法登录都付出代价,当繁忙的端点对每个请求进行哈希时,这一点很重要。
因为成本是二的幂,所以每一步的工作都会加倍。成本 11 的速度是成本 10 的两倍,成本 12 的速度是成本的四倍。在典型的 2020 年代服务器硬件上,成本 10 到 12 落在正确的邻域,这就是为什么框架默认工具会显示粗略的时间估计,因为您拖动滑块,这样您就可以感受到成本复合的速度有多快,但将其视为相对指南,而不是服务器的基准。真实数字取决于您的 CPU,唯一知道的方法就是时间 password_hash 在将运行它的盒子上。
1个实用模式值得了解:因为成本存储在每个哈希内部,你可以在不迁移的情况下随着时间的推移提高它,在成功登录时你手里有一瞬间明文,所以你检查存储的哈希's成本是否低于你当前的目标,如果是,重新哈希和更新。PHP直接通过暴露这一点 password_needs_rehash。这意味着您的安全性与硬件保持同步,而无需强制重置密码。
Bcrypt 与 SHA-256、Argon2 和 scrypt 的对比
Bcrypt 对一个普通的快速哈希, 以及对较新的内存硬函数最重要的比较, 以下是它们如何对密码存储进行排队:
| 功能 | 盐腌 | 目的缓慢 | 记忆力强 | 适合密码吗? |
|---|---|---|---|---|
| MD5/SHA-256 | 不(自己) | 否 | 否 | 不,太快了 |
| b加密 | 是的,内置 | 是的,可调整的成本 | 否 | 是的,推荐 |
| 抄袭 | 是的 | 是的 | 是的 | 是的 |
| 氩2id | 是的 | 是的 | 是的 | 是的,更适合新系统 |
裸露的 SHA-256 密码,甚至是加盐的,是错误的工具:它是为速度而设计的,因此泄露的加盐 SHA-256 哈希数据库很快就会落入 GPU。如果您只需要一个快速摘要来获取校验和或缓存密钥而不是密码,那就是密码 哈希生成器 is for,而区分是全部 2015密码散列大赛冠军Argon2id增加了内存硬度,它抵制了可以加速bcrypt的自定义硬件,并且是控制运行时间的全新系统的首选,但是bcrypt并没有过时,它经过了战斗测试,它是我日常使用的工具中的默认值,选择它是一个合理的决定,特别是当你的框架发货时,要避免的错误不是"bcrypt而不是Argon2,"它是"快速散列而不是任何一个。"对于与散列配对的安全实用程序进行更广泛的游览, 开发人员生产力工具 导游有地图。
72字节的限制和其他gotchas呢?
Bcrypt 只读取密码的前 72 字节,任何被默默忽略的过去,对于普通密码来说从来都不重要,但可以咬一个很长的密码短语,尤其是具有多字节 UTF-8 字符的工具,字节计数爬升速度比字符计数快,每当您的输入跨越 72 UTF-8 字节时,该工具就会警告您,因此截断永远不会是一个无声的惊喜。有些系统在将密码交给 bcrypt 之前,通过 SHA-256 和 Base64 编码预先散列密码,从而绕过极限,bcrypt 将任意长的密码折叠成固定的短输入;如果这样做,请在注册和登录时始终如一地执行。
另一个gotcha是版本前缀。你会看到的 $2a$、 $2y$、和 $2b$ 在野外。 $2a$ 是最初的, $2y$ 是在一些实现中发现符号扩展错误后引入的 PHP 特定标签,并且 $2b$ 是当前修正的openbsd版本。对于您想要的新哈希值 $2b$(即此工具产生的)。这三者都对普通的 ASCII 密码进行相同的验证;差异仅在 72 字节边界附近与特定的非 ASCII 字节一起浮出水面。当您将令牌粘贴到一个也检查结构化凭据的工具中时,例如 JWT 解码器请记住,bcrypt 哈希不是您解码以读取有效负载的令牌;它是一个单向摘要,没有任何内容可供揭示。
Bcrypt生成器适合真实工作流程的地方
On itselves a hash 只是故事的一半,所以混音器与我保持打开的其他凭据工具并排,当我播种测试数据库或设置模拟帐户时,我会使用 the 生成密码本身 密码生成器,检查它是否清除带有 的合理强度条 密码强度检查器并且只有这样才能运行它 b加密生成器 要获取存储的哈希。该订单很重要:cost-12 bcrypt 哈希背后的强随机密码确实很难破解,而同一哈希背后的弱密码仍然会落入字典攻击,因为攻击者只需猜测少数可能的候选者而不是整个密钥空间的成本因素即可为您赢得时间;密码熵就是该时间所花费的时间。
verify模式在调试时赚取它的位置 当登录在分期中神秘失败时,隔离问题的最快方法是粘贴存储的哈希和您认为正确的密码,看看它们是否匹配。匹配意味着错误在上游,在密码如何到达支票;不匹配意味着存储的哈希是用不同的密码或损坏的编码编写的。要么答案可以让你免于猜测,而且由于整个事情在本地运行,您可以用从数据库中提取的真实哈希来完成它,而不会让该哈希离开您的机器。这是站点上每个工具背后的相同客户端原则,对于任何触及凭据的东西都不是一个好东西,这是要求。
常见问题
在这个 bcrypt 生成器中散列真实密码安全吗?
是的,因为散列完全发生在您的浏览器中,并且没有任何内容发送到任何地方。您可以在开发人员工具的网络选项卡中确认这一点:生成散列不会提出任何请求。也就是说,浏览器选项卡不是密码管理器,因此请避免将生产帐户的实时凭据粘贴到您未构建的任何网页中,并在完成后关闭该选项卡。
为什么相同的密码每次都会产生不同的 bcrypt 哈希值?
Bcrypt因为每个哈希都会生成一个新的随机盐,并混合到结果中,这是一个功能:选择相同密码的两个用户仍然会得到不同的存储哈希,因此攻击者无法发现共享密码或重用预先计算的表验证仍然有效,因为盐存储在哈希字符串本身内部。
Bcrypt应该使用什么成本因素?
用登录流可以容忍的最高成本,而不会出现令人讨厌的延迟,对于大多数2020年代的服务器来说,这需要10到12。OWASP建议,在生产硬件上,单个哈希应该大约需要四分之一秒。因为每一步都将工作加倍,在自己的服务器上进行测量而不是复制一个数字,并且随着硬件的加速,每隔几年就会提高成本。
Bcrypt 哈希值可以反转回密码吗?
No。Bcrypt是一个单向函数,所以没有数学方法可以将哈希值变回原始密码,唯一的攻击就是猜测:攻击者将相同盐和成本的候选密码哈希,并检查匹配成本因素,这就是为什么成本因素很重要,因为它设置了每个猜测有多昂贵,以及为什么强唯一密码加上高成本会使猜测变得不切实际。
2026年bcrypt是否仍然安全,还是应该切换到Argon2?
Bcrypt 仍然被认为是密码存储的安全,并且仍然是 OWASP 密码存储速查表中的推荐选项。2015 年密码散列竞赛的获胜者 Argon2id 是您控制运行时的新系统的首选,因为它还抵御 GPU 和专用硬件攻击。Bcrypt 是一个合理的选择,特别是当您的框架将其作为默认值导出时;真正的错误是使用快速通用散列而不是任何密码散列功能。
bcrypt 中的 72 字节限制是多少?
Bcrypt只读取UTF-8编码密码的前72字节,因此任何超出该字节的字符都会被忽略,对于普通密码这永远不重要,但可以截断一个很长的密码短语,这就是为什么有些系统在bcrypt之前用SHA-256预先散列密码,每当输入超过72字节时,该工具就会警告您,因此截断永远不会令人惊讶。
2a$、2b$、2y$前缀是什么意思?
是bcrypt输出格式的版本标签,$2a$是原始的,$2y$是针对符号扩展错误的PHP专用修复,$2b$是当前修正的OpenBSD版本,对于新的哈希值$2b$是正确的,这也是该工具生成的三个都对ASCII密码进行相同的验证;差异仅出现在72字节边界的某些非ASCII字节。
B加密和加密一样吗?
No。加密是用密钥可逆的,所以加密的数据可以解密回原来的,bcrypt散列是单向的,故意慢,而且只用于验证:你永远不需要恢复密码,只需要确认有人输入了正确的密码,用加密而不是散列来存储密码是一种反模式,因为谁持有密钥,谁就可以恢复每一个密码。



