ChatGPT解决这个技术问题 Extra ChatGPT

我应该使用什么列类型/长度将 Bcrypt 散列密码存储在数据库中?

我想在数据库中存储一个散列密码(使用 BCrypt)。什么是一个好的类型,哪个是正确的长度?使用 BCrypt 散列的密码是否总是相同的长度?

编辑

示例哈希:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

在对一些密码进行哈希处理后,BCrypt 似乎总是生成 60 个字符的哈希值。

编辑 2

很抱歉没有提到实施。我正在使用 jBCrypt

另请参阅 Openwall 的 PHP password hashing framework (PHPPass)。它的便携性和强化了针对用户密码的一些常见攻击。编写框架 (SolarDesigner) 的人与编写 John The Ripper 并在 Password Hashing Competition 中担任评委的人是同一个人。所以他知道关于密码攻击的一两件事。
如果有人为此寻找 scrypt 的解决方案:Gumbo 的答案也适用于 scrypt。我个人在 MySQL 中应用了 BINARY(64),它允许我稍后在 Python 下测试字节相等性。

C
Community

bcrypt 的模块化 crypt 格式包括

$2$、$2a$ 或 $2y$ 标识哈希算法和格式

表示成本参数的两位数字值,后跟 $

一个 53 个字符长的 base-64 编码值(它们使用字母表 .、/、0-9、A-Z、a-z,这与标准 Base 64 编码字母表不同)包括:22 个盐字符(有效132 个解码位中只有 128 位)加密输出的 31 个字符(实际上只有 186 个解码位中的 184 位)

22 个 salt 字符(实际上只有 132 个解码位中的 128 个位)

31 个字符的加密输出(实际上只有 186 个解码位中的 184 个位)

因此总长度分别为 59 或 60 个字节。

当您使用 2a 格式时,您将需要 60 个字节。因此对于 MySQL,我建议使用 CHAR(60) BINARYor BINARY(60)(有关差异的信息,请参阅 The _bin and binary Collations)。

CHAR 不是二进制安全的,相等性不仅仅取决于字节值,还取决于实际的排序规则;在最坏的情况下,A 被视为等于 a。有关详细信息,请参阅 The _bin and binary Collations


请注意 - 存储为二进制(60)可能会导致字符串相等的意外行为(除其他外)。在 .NET 中,这可以通过使用 String.Equals(fromDataBaseBinary60string,typicalishString, StringComparison.InvariantCulture) 来克服
如果将该列定义为 CHAR(60) CHARACTER SET latin1 COLLATE latin1_bin,您现在可以获得准确字符串比较的优势,而无需二进制列。
@AndreFigueiredo SQL_Latin1_General_CP1_CS_AS 在 MySQL 中是未知的。已知的是latin1_general_cs
我不清楚我们应该存储为不是二进制安全的 char 还是具有意外行为的 binary(60) .....
不应该比较密码检查的哈希值。我遇到的每个 bcrypt 库也都有密码检查功能。可以将密码存储为字符串(即使用 CHAR 或 VARCHAR 或 VARCHAR2 的字符数组)并使用库的密码检查功能将该字符串与用户提供的密码进行比较。
A
Andre D

Bcrypt 哈希可以存储在 BINARY(40) 列中。

BINARY(60),正如其他答案所暗示的那样,是最简单和最自然的选择,但如果您想最大限度地提高存储效率,您可以通过无损解构散列来节省 20 个字节。我在 GitHub 上对此进行了更详尽的记录:https://github.com/ademarre/binary-mcf

Bcrypt 哈希遵循称为模块化密码格式 (MCF) 的结构。二进制 MCF (BMCF) 将这些文本哈希表示解码为更紧凑的二进制结构。在 Bcrypt 的情况下,生成的二进制哈希是 40 字节。

Gumbo 很好地解释了 Bcrypt MCF 哈希的四个组成部分:

$<id>$<cost>$<salt><digest>

解码为 BMCF 如下所示:

$$ 可以用 3 位表示。 $, 04-31, 可以用 5 位表示。将这些放在一起 1 个字节。 22 个字符的盐是 128 位的(非标准)base-64 表示。 Base-64 解码产生 16 个字节。 31 个字符的散列摘要可以 base-64 解码为 23 个字节。将它们放在一起 40 个字节:1 + 16 + 23

您可以在上面的链接中阅读更多内容,或者查看 my PHP implementation,也可以在 GitHub 上查看。


更长字段的成本:20 字节乘以甚至一百万条记录+ 记录:20MB,一旦达到一百万条记录+。在高度复杂的安全和工程领域中不正确地实施缩短的字段长度的成本: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$你算算。
@Kzqai,就像我说的,更大的 60 字节列是最自然的选择,但是如何积极地追求存储效率取决于项目。例如,尝试将整个数据库放入内存是很常见的,在内存受限的环境中,这里的 20 MB 和那里的另外 20 MB 可以快速加起来。
你的例子符合我的观点。 --- 如果要将数据库放入内存,请在接触 bcrypt 存储列之前优化每隔一列。 --- 如果你已经将其他列优化到了疯狂的程度,并且只剩下 bcrypt 哈希列,那么再为 bcrypt 获取另一块内存。 --- 如果您已经完成了上述两项... ...停下来,您还没有优化其他所有低效的果实,并且您将弄乱经过测试的有效加密安全系统,并替换它具有更复杂的本土系统,实施失败的可能性。
@Kzqai 这里没有削弱 Bcrypt 库安全性的风险。这是一种数据编码,在密码检查之前从存储中检索时会被撤消。这不是“不要推出自己的加密货币”领域。
很好的解释。 :) 虽然你的解释给出了一个好主意,但为了安全起见,我只想使用 60 个字符,甚至 100 个字符。很好的辩论@Kzqai 和 AndreD
M
Mike

如果您将 PHP 的 password_hash()PASSWORD_DEFAULT 算法一起使用来生成 bcrypt 哈希(我假设大部分人会阅读此问题),请务必记住,将来 password_hash() 可能会使用不同的算法作为默认值,因此这可能会影响散列的长度(但不一定更长)。

从手册页:

请注意,此常量旨在随着 PHP 中添加新的和更强大的算法而随时间而变化。因此,使用此标识符的结果长度可能会随时间而变化。因此,建议将结果存储在可以扩展超过 60 个字符的数据库列中(255 个字符将是一个不错的选择)。

使用 bcrypt,即使你有 10 亿用户(即你目前正在与 facebook 竞争)来存储 255 字节的密码哈希,它也只会存储大约 255 GB 的数据——大约是一个小型 SSD 硬盘的大小。存储密码哈希极不可能成为应用程序的瓶颈。但是,如果由于某种原因存储空间确实问题,您可以使用 PASSWORD_BCRYPT 强制 password_hash() 使用 bcrypt,即使这不是默认设置。请务必随时了解 bcrypt 中发现的任何漏洞,并在每次发布新的 PHP 版本时查看发行说明。如果默认算法发生更改,最好回顾一下为什么并做出是否使用新算法的明智决定。


J
James C

我不认为您可以使用任何巧妙的技巧来存储它,例如使用 MD5 哈希。

我认为您最好的选择是将其存储为 CHAR(60) 因为它总是 60 个字符长


尽管 PHP 文档指出列应该能够容纳更多数据,但对于未来的版本......
没有理由去镀金。如果您使用的软件需要 60 个字节,则分配 60 个字节。如果您的软件的未来版本会改变这一点,那么当该版本发生时您可以担心它。您不应该自动安装更改功能的更新。
我认为这是最好的答案。无需像其他答案那样深入研究算法的复杂性。所有关于二进制、排序规则等的细节都应该由正在使用的任何库处理。 60 个字符。这就是答案。
请注意,对于某些数据库(postgresql),“大小”列不是强制性的。