在MySQL中存储SHA1哈希值

162

我有一个简单的问题,当我想把SHA1哈希值存储到MySQL数据库中时,它出现了:

我应该为存储哈希结果的VARCHAR字段设置多长长度?


10
如果你刚刚谷歌了sha1,点击“I'm feeling lucky”按钮,你会被带到维基百科的页面,那里可以找到它始终是160位。 - Tim Matthews
7个回答

320
我会对可变长度数据使用VARCHAR,但不会对固定长度数据使用。因为SHA-1值始终是160位长,VARCHAR只会浪费一个额外的字节用于固定长度字段的长度

我也不会存储SHA1返回的值。因为它每个字符只使用4位,因此需要160/4 = 40个字符。但如果您每个字符使用8位,则只需要一个160/8 = 20字符长的字段。

因此,我建议您使用BINARY(20)UNHEX函数SHA1值转换为二进制。

我比较了BINARY(20)CHAR(40)的存储要求。

CREATE TABLE `binary` (
    `id` int unsigned auto_increment primary key,
    `password` binary(20) not null
);
CREATE TABLE `char` (
    `id` int unsigned auto_increment primary key,
    `password` char(40) not null
);

使用InnoDB引擎,百万条记录中binary(20)占用44.56M,而char(40)占用64.57M。


2
在PostgreSQL中,这将转换为使用bytea字段,对吗? - mvexel
1
这个解决方案很好,但是使用char(40)与hexed sha1有另一个优点——它更为广泛地使用,在应用程序代码中会有更少的转换问题。 - Arthur Kushman
3
针对phpMyAdmin用户的提示:当以二进制形式存储哈希值时,phpMyAdmin会将其显示为十六进制字符串,但pma将无法在提供的“搜索选项卡”中使用它。只有手动将UNHEX()添加到SQL语句中才能正常工作。 - Timo Huovinen
2
@Gumbo,你可以在bytea中存储可变数量的字节。你所指的是bytea类型的存储要求,即“1或4个字节加上实际的二进制字符串”。 “1或4”所指的可能是存储的数据长度,因为你不能像varchar一样使用零字节来结束字符串。这意味着,但手册中没有说明,你可以在bytea中存储多达2^(8*4)或4+ GB的数据。http://www.postgresql.org/docs/9.0/static/datatype-binary.html将哈希值存储在postgres数据库中,最好使用_bit_或bytea列。 - Viktor
2
http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html#function_sha1 提供了有关存储加密函数结果时的性能和存储信息。 - Clocker
显示剩余4条评论

45

SHA1哈希值长度为40个字符!


11

参考来源于此博客:

以下是哈希算法及其所需位数的列表:

  • MD5 = 128位哈希值。
  • SHA1 = 160位哈希值。
  • SHA224 = 224位哈希值。
  • SHA256 = 256位哈希值。
  • SHA384 = 384位哈希值。
  • SHA512 = 512位哈希值。

创建一个需要 CHAR(n) 的示例表:

CREATE TABLE tbl_PasswordDataType
(
    ID INTEGER
    ,MD5_128_bit CHAR(32)
    ,SHA_160_bit CHAR(40)
    ,SHA_224_bit CHAR(56)
    ,SHA_256_bit CHAR(64)
    ,SHA_384_bit CHAR(96)
    ,SHA_512_bit CHAR(128)
); 
INSERT INTO tbl_PasswordDataType
VALUES 
(
    1
    ,MD5('SamplePass_WithAddedSalt')
    ,SHA1('SamplePass_WithAddedSalt')
    ,SHA2('SamplePass_WithAddedSalt',224)
    ,SHA2('SamplePass_WithAddedSalt',256)
    ,SHA2('SamplePass_WithAddedSalt',384)
    ,SHA2('SamplePass_WithAddedSalt',512)
);

11
请,请,千万不要将密码以这种方式存储。 - Berry M.
嘿,贝瑞,你能详细解释一下你的为什么吗? - Anvesh
4
如果你只是简单地存储了密码的哈希值,而没有采用加盐(并且最好采用拉伸过的)密码哈希算法,那么如果你的数据库被攻破,这些密码就会更容易被“提取”。建议阅读:https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016。 - matt
2
@BerryM。一年后看到这条消息,我从未想过有人会谈论密码或者人们是否仍然使用简单的哈希算法来存储认证数据。但是他们确实这样做:D - Rohit Hazra

6

SHA1的输出大小为160位。如果您使用8位字符,则为160/8 = 20个字符,如果您使用16位字符,则为160/16 = 10个字符。


假设使用8位二进制字符,如果以十六进制形式存储,则需要40个字符。 - Tyzoid

3

因此,长度介于10个16位字符和40个十六进制数字之间。

无论如何,决定您要存储的格式,并根据该格式设置字段的固定大小。这样,您就不会浪费任何空间。


2

在某些情况下,您可能仍希望使用VARCHAR,例如在不始终为用户存储哈希值的情况下(即身份验证帐户/忘记登录URL)。 一旦用户已经验证/更改了他们的登录信息,他们就不应该能够使用哈希,并且也没有理由这样做。 您可以创建一个单独的表来存储临时哈希 -> 用户关联,但我认为大多数人不会费心去这样做。


2
如果您需要在sha1列上创建索引,出于性能原因,我建议选择CHAR(40)。 在我的情况下,sha1列是电子邮件确认令牌,因此在着陆页面上,查询仅使用令牌进入。 在这种情况下,我认为带有INDEX的CHAR(40)是最佳选择 :) 如果您想采用此方法,请记得将$raw_output = false。

1
为什么不对BINARY(20)进行索引?这样做不是同样快而且大小只有一半吗? - nickdnk
大约5年前我提到的是你仍然需要解码,这会增加一些负担(+使应用程序更难维护和不太可移植?)。这也取决于你的硬件,如果存储空间较少且速度较慢,最好还是使用二进制(20),否则我建议使用char(40)。没有在使用语言和硬件上运行一些测试来确定最适合您的选项,很难说。 - Francesco Casula
1
我想,如果你要做的不是从where unhex(hash) = hash选择获取单行数据,那么你可能是对的。但是这种方式会占用两倍的内存来保持索引缓存。 - nickdnk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接