关于mysql:我应该使用什么列类型/长度在数据库中存储Bcrypt哈希密码?

What column type/length should I use for storing a Bcrypt hashed password in a Database?

我想在数据库中存储哈希密码(使用bcrypt)。哪种类型适合这个,哪种长度合适?密码与bcrypt的哈希值是否总是相同的长度?

编辑

哈希示例:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

在散列一些密码之后,bcrypt似乎总是生成60个字符的散列。

编辑2

很抱歉没有提到实现。我正在使用jbcrypt。


bcrypt的模块化密码格式包括

  • $2$$2a$$2y$标识哈希算法和格式
  • 表示成本参数的两位数值,后跟$
  • 一种53个字符长的base-64编码值(它们使用的字母是./09AZAZ,不同于标准的base 64编码字母),包括:
    • 22个字符的salt(实际上只有132个解码位中的128位)
    • 31个字符的加密输出(实际上186位解码中只有184位)

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

使用2a格式时,需要60个字节。因此,对于MySQL,我建议使用CHAR(60) BINARYBINARY(60)(有关差异的信息,请参阅二进制和二进制排序)。

CHAR不是二进制安全的,相等不仅取决于字节值,而且取决于实际的排序规则;在最坏的情况下,A被视为等同于A。有关更多信息,请参阅_binbinary整理。


bcrypt散列可以存储在BINARY(40)列中。

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

Bcrypt哈希遵循一种称为模块化密码格式(MCF)的结构。二进制MCF(BMCF)将这些文本哈希表示解码为更紧凑的二进制结构。对于bcrypt,生成的二进制散列为40个字节。

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

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

对BMCF的解码如下:

  • $$可以用3位表示。
  • $04-31可以用5位表示。把这些放在一起1个字节。
  • 22个字符的salt是128位的(非标准)base-64表示。Base-64解码产生16个字节。
  • 31个字符的哈希摘要可以是base-64,解码为23个字节。
  • 总共40个字节:1 + 16 + 23
  • 您可以在上面的链接中阅读更多内容,或者在Github上检查我的PHP实现。


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

    从手册页面:

    Note that this constant is designed to change over time as new and
    stronger algorithms are added to PHP. For that reason, the length of
    the result from using this identifier can change over time. Therefore,
    it is recommended to store the result in a database column that can
    expand beyond 60 characters (255 characters would be a good choice).

    使用bcrypt,即使你有10亿用户(即你目前正在与Facebook竞争)来存储255字节的密码散列,它也只能存储约255GB的数据,大约相当于一个小型的SSD硬盘大小。存储密码哈希不太可能成为应用程序的瓶颈。然而,由于某些原因,存储空间确实存在问题,您可以使用PASSWORD_BCRYPT强制password_hash()使用bcrypt,即使这不是默认值。只要确保随时了解bcrypt中发现的任何漏洞,并在每次发布新的PHP版本时查看发行说明。如果默认算法曾经被更改过,那么最好检查原因并做出是否使用新算法的明智决定。


    我不认为您可以像使用MD5散列那样,使用任何巧妙的技巧来存储它。

    我认为你最好的办法是把它作为一个CHAR(60)存储起来,因为它总是有60个字符长