一个盐应该有多长才能使尝试字典攻击变得不可行?

15

我正在设计一个认证系统,它的工作原理如下:

  1. 用户输入密码
  2. 生成盐值。
  3. 使用whirlpool算法对密码进行哈希处理
  4. 将whirlpool哈希处理后的密码与明文盐值连接在一起
  5. 使用sha1算法对连接后的版本进行哈希处理,并将结果存储在数据库中。
  6. 我通过在应用程序层对密码进行哈希处理,然后执行以下操作(在MySQL中)来检查密码是否正确:

MySQL

WHERE `Password` = SHA1(CONCAT('$hashedPassword',`Salt`)) AND [..]

目前我的盐值是64字节。这样是否足以使字典攻击变得不可行?

我知道sha1有已知的漏洞,但它是我使用的MySQL版本(5.1)中唯一可用于数据库层的函数,而不是在应用程序和数据库层之间选择明文盐。


是的,哈希密码和盐值的SHA1结果存储在密码字段中。 - Will Morgan
@Will Morgan:那么,你打算如何验证密码呢??你必须将盐存储在单独的数据字段中。此外,盐的长度并不太重要,只要足够长,以至于已经存在彩虹表的概率非常低(8个字节应该足够了)。 - Niklas B.
请参见上面的答案 - Salt 引用的是字段本身,而不是字面字符串。 - Will Morgan
@WillMorgan:好的,我现在明白了。 - Niklas B.
我永远不会使用超过16字节的盐。假设您的加密和哈希算法永远不会被破解,因此暴力攻击是对手唯一的选择。一旦您的盐为16字节,对手已经比攻击盐更聪明地直接攻击密钥进行暴力攻击,成功攻击仍无法使其读取明文(尽管它会更接近)。如果您的盐与随机值相当接近,我想不出任何理由需要在其中使用比密钥本身更多的字节。 - WDS
显示剩余3条评论
7个回答

19

我认为你误解了“盐”的概念。盐并不能显著地防止或减缓字典和暴力攻击

使用盐的整个目的是避免有人已经预先计算出了您密码哈希的字典/暴力破解攻击(例如使用彩虹表)的可能性。因此,它只需要足够长以排除这样的表已经存在于特定盐中的可能性。

考虑到这种彩虹表的典型大小,对于像8个字节大小的盐这样小的大小已经预先计算了这些表格的人非常不可能(考虑可能的盐的数量:256^8 = 18446744073709551616)。前提是盐是随机生成的并且您不重复使用相同的盐值。当然,64个字节也无妨,没有任何问题。

但是,如果您想使暴力破解或字典攻击变得困难,那么使用更长的盐将无济于事。相反,让用户选择强密码,并考虑使用密钥延伸


2
盐值对单个用户的攻击并没有显著的减缓作用,但是对于大型数据库攻击则有一定的减缓作用。考虑一个没有盐值的数据库,现在你从常见密码开始,计算一次哈希值,然后将其与整个数据库进行比对。接着再尝试下一个常见密码。这种方法在使用盐值时是行不通的。 - Cruncher

8
我的 Practical Cryptography 副本(Ferguson,Schneier),版权日期为2003年,建议使用256位(32字节)的盐长度。它说128位“可能”可以,但是如它所指出的,比特很便宜。考虑到每个密码在磁盘上存储64字节的盐的相对较小成本似乎是合理的。这可能有点过度保护,但不会有坏处。
您还可以考虑密码延伸技术(多次重复哈希函数)以增加攻击密码的计算复杂性,通过暴力破解密码来增加几百毫秒的检查成本可以极大地增加暴力攻击的成本。

维基百科关于“密钥拉伸”的文章是错误的。它描述了密钥强化。密钥拉伸是一种允许您派生出比底层哈希函数输出大小更长的输出的方法,也是将PKCS#5 PBKDF2与PBKDF1区分开来的因素之一。 - Henrick Hellström
@HenrickHellström:它所描述的密钥拉伸与我所见到的描述一致。我也看到过它被称为“密码拉伸”(就像我在这里提到的那样)。 - Mark Wilkins
2
那并不代表它是正确的,这种用法肯定会让不熟悉密码学的读者感到困惑。当您指定高迭代次数时,密钥强化是您所做的事情。当您想要一个特定长度的派生密钥时,密钥拉伸是您所做的事情。如果您使用PBKDF为AES-256-CBC加密生成256位密钥,则已执行密钥拉伸,但不一定进行了密钥强化。仅仅因为派生密钥是256位,并不意味着您一定获得了256位的安全性。 - Henrick Hellström
1
@HenrickHellström:我同意正确使用这些术语很重要。你有这些术语的权威参考资料吗?我现在注意到这个问题的被接受答案使用了相同的术语并引用了相同的维基页面(或指向相同页面的一个页面)。 - Mark Wilkins
1
我认为将“key stretching”这个术语滥用于密钥强化的做法可以追溯到2005年Frances F. Yao和Yiqun Lisa Yin发表的一篇文章,其中他们讨论了迭代作为“拉伸”密码熵的一种方法。在这里可以找到“key stretching”作为密钥扩展的同义词,例如:http://www.rfc-editor.org/rfc/pdfrfc/rfc5931.txt.pdf。 - Henrick Hellström
1
使用 "key stretching" 作为密钥扩展的同义词的另一个示例:http://tools.ietf.org/html/draft-krovetz-umac-01。还有一个:http://www.gnu.org/software/gnu-crypto/manual/api/gnu/crypto/prng/UMacGenerator.html - Henrick Hellström

4
盐值决定了预先计算表(例如彩虹表)所需的存储空间,攻击者可通过该表快速查找给定哈希值的密码。哈希迭代次数(而非盐值)决定了攻击者尝试字典中每个密码所需的时间。
每增加一个盐值比特位,都会使查找表所需的空间翻倍。因此,8个字节(64位)将导致空间乘数达到1600万TB,总空间超过yottabyte范围(可能超出大多数攻击者的能力范围)。

1
彩虹表不会自己生成,它们需要计算。使用更长的盐还显著增加了创建密码彩虹表所需的时间(直到在第一次生成这些表变得不可行的程度)。即使有足够的空间来存储假设的尧字节表,也很难拥有足够的计算能力甚至存储带宽来首先生成它。 - Niklas B.
如何生成彩虹表以攻击加盐密码? - erickson
你可以为每个可能的盐值创建单独的表,或者(在假设的8字节哈希的情况下)找到一个减少函数,将哈希值转换为盐和密码空间的笛卡尔积。然而,后者可能有点不必要,因为攻击者应该知道盐。但是,这不是我的重点,我只想补充说,盐的熵不仅会指数级增加存储查找表所需的空间,还会指数级增加生成这些表所需的时间,这至少同样重要。 - Niklas B.
这一切都是无意义的,因为盐使预计算变得不可行。没有人能够负担得起计算表格所需的能源或存储空间。 - erickson

1

盐用于向密码添加额外的随机位以使攻击更加低效。 因此,盐添加的熵越多,就越好。

目前,PKCS#5建议盐长度至少为64位熵,通常推荐的bcrypt使用128位,甚至可以使用更多。 但是,当已经获得乌托邦级别的结果时,肯定有一个限度,不会增加额外的实际复杂性。

因此,每个密码应该至少有一个唯一的盐,这样一次只能破解一个密码。 最好使用已经证明过的密码存储方案。


1

书籍:

密码学工程:设计原理与实际应用

作者:尼尔斯·弗格森,布鲁斯·施奈尔和河野忠义

21.2.1 盐和拉伸

由于比特币便宜,为了简单起见,我们建议使用256位的盐。


0

8个字节足够了。

当你在Linux(内核版本3.16)中查看/etc/shadow文件时,可以看到用户密码保存的形式如下:

$id$salt$encrypted_password

  • id定义了哈希算法

在我的Linux版本中,盐值是8位数字,即8个字节。我认为内核开发人员知道他们在做什么,所以我也使用8个字节作为我的盐值,这些字节来自一个具有密码学安全性的伪随机生成器源。在Linux中,你可以简单地读取/dev/random(当熵值低时会阻塞)或/dev/urandom(不会阻塞)。

要获取更多信息,请阅读crypt的man页面。


0
考虑你的应用程序有80亿用户(当前世界人口)。今天,全球的存储容量约为10 ZB(来源:IDC),每4年翻一倍,这意味着到2064年左右将达到约13800 ZB。考虑一个非常小的1MB彩虹表作为攻击字典。64位盐需要18446 ZB的存储空间来存储彩虹表(1MB * 2^64)。因此,64位盐足以使用41年,96位盐足以使用161年。使用像Argon2这样的慢哈希算法也会增加创建表的初始成本。

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