在数据库中存储Bcrypt哈希密码时应使用哪种列类型/长度?


317

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

编辑

哈希示例:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

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

编辑2

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


另请参见Openwall的PHP密码哈希框架(PHPass)。它具有可移植性,并且可以抵抗多种常见的用户密码攻击。编写框架的人(SolarDesigner)与编写John The Ripper的人相同,并担任密码哈希竞赛的评委。因此,他知道有关密码攻击的一两件事。
jww

1
如果有人为此寻求scrypt解决方案:Gumbo的答案也适用于scrypt。我个人在MySQL中应用了BINARY(64),它稍后使我能够在Python下测试字节相等性。
菲利普·赫伯特

Answers:


368

bcrypt的模块化crypt格式包括

  • $2$$2a$$2y$识别哈希算法和格式
  • 表示费用参数的两位数字值,后跟 $
  • 一个53字符长的基64编码的值(它们使用的字母表./0- 9A- Za- z也就是不同的标准Base 64编码字母表)由以下组成:
    • 盐的22个字符(有效地是132个解码位中的128个位)
    • 31个字符的加密输出(有效的是186个解码位中的184位)

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

使用2a格式时,需要60个字节。因而对于MySQL的,我会建议使用CHAR(60) BINARYBINARY(60)(见_bin二进制排序规则有关的差异信息)。

CHAR不是二进制安全的,相等性不仅仅取决于字节值,还取决于实际的排序规则;在最坏的情况下A被视为等于a。见_binbinary排序规则以获取更多信息。


28
请注意-以二进制(60)形式存储可能会导致字符串相等性发生意外行为(除其他外)。在.NET中,可以使用String.Equals(fromDataBaseBinary60string,典型的字符串,StringComparison.InvariantCulture)
JHubbard80

8
如果将列定义为CHAR(60)CHARACTER SET latin1 COLLATE latin1_bin,您现在将获得准确的字符串比较的优势,而无需二进制列。

2
@AndreFigueiredo SQL_Latin1_General_CP1_CS_AS在MySQL中未知。已知的是latin1_general_cs
2015年

1
我喜欢这里有一个定义是什么22a以及2y平均为哈希算法和格式。我无法通过一些搜索找到一个简单的答案。
jocull

2
@Neon问题是您可能将不同的哈希值比较为相等。如果您明确指定它是二进制列(或具有正确排序规则的VARCHAR),则无需冒着在其他地方更改一些使其不区分大小写的设置的风险。这也使您的意图更加清晰,这通常是一件好事-您正在存储二进制数据;您应该将其存储为二进制数据。
基金莫妮卡的诉讼

51

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就像这样:

  1. $<id>$ 可以用3位表示
  2. <cost>$,04-31,可以用5位表示。将它们放在一起1个字节。
  3. 22个字符的盐是128位的(非标准)base-64表示形式。Base-64解码产生16个字节。
  4. 31个字符的哈希摘要可被base-64解码为23个字节。
  5. 放在一起总共40个字节: 1 + 16 + 23

您可以在上面的链接上阅读更多内容,或者在GitHub上检查我的PHP实现


49
较长字段的成本:20字节乘以一百万甚至更多的记录:20 MB,一旦达到一百万记录+。在高度复杂的安全和工程领域中,不正确地实施缩短的字段长度的成本:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$计算。
喀恰

6
就像我说的那样,@ Kzqai来说,更大的60字节列是最自然的选择,但是如何积极地提高存储效率取决于项目。例如,通常尝试将整个数据库放入内存中,在内存受限的环境中,此处20 MB和那里20 MB可能会迅速增加。
安德烈D

10
您的例子说明了我的观点。---如果要将数据库放入内存,请在触摸bcrypt存储列之前优化所有其他列。---如果您已将所有其他列优化到疯狂程度,并且仅保留了bcrypt哈希列,请为bcrypt获得另一个内存。---如果您同时完成了上述两项工作,则... ...并没有优化所有其他的低挂水果,并且您将无法使用经过测试的加密安全系统,并进行更换它与更复杂的本地系统一起使用,可能会导致实现失败。
喀恰

11
@Kzqai这里没有削弱Bcrypt库安全性的风险。这是一种数据编码,在进行密码检查之前,无法从存储中进行检索。这不是“不要自行开发加密货币”领域。
安德烈D

1
很好的解释。:)尽管您的解释给出了一个很好的主意,但为了安全起见,我只想带60个字符,甚至100个字符。@Kzqai和AndreD的辩论也不错
Naveen Kumar V

23

如果您将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版本时查看发行说明。如果更改了默认算法,则最好回顾原因并做出明智的决定,以决定是否使用新算法。


20

我不认为可以像使用MD5哈希那样进行任何巧妙的操作来存储它。

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


虽然,PHP文档指出,列对于将来的发行版应该能够容纳更多数据……
朱利安·F·韦纳特

16
没有理由镀金。如果您使用的软件需要60个字节,则分配60个字节。如果您的软件有将来的发行版可以对此进行更改,那么当该发行版出现时,您可以担心它。您不应该自动安装功能更改更新。
泰勒·克伦普顿
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.