关于安全:为什么盐使字典攻击“不可能”?

Why do salts make dictionary attacks 'impossible'?

更新:请注意,我不是在问什么是盐,什么是彩虹表,什么是字典攻击,或者盐的用途。我在问:如果你知道用户salt和hash,计算他们的密码不是很容易吗?

我了解这个过程,并在我的一些项目中自己实现它。

1
2
s =  random salt
storedPassword = sha1(password + s)

在存储的数据库中:

1
username | hashed_password | salt

我看到的每一个salt实现都会在密码末尾或开头添加salt:

1
2
hashed_Password = sha1(s + password )
hashed_Password = sha1(password + s)

因此,一个值得使用salt(ha ha ha)的黑客对字典进行攻击,只需对上面列出的常见组合中存储的salt运行每个关键字。

当然,上面描述的实现只是为黑客添加了另一个步骤,而没有实际解决底层问题?有什么方法可以解决这个问题,还是我误解了这个问题?

我唯一能想到的是有一个秘密的混合算法,将salt和密码以随机模式放在一起,或者在哈希过程中添加其他用户字段,这意味着黑客必须访问数据库和代码才能放在这些字段上,这样字典攻击才能证明是有效的。(更新,正如评论中指出的,最好假设黑客可以访问您的所有信息,所以这可能不是最好的)。

让我举一个例子,说明我如何建议黑客使用密码和哈希列表对用户数据库进行黑客攻击:

来自黑客数据库的数据:

1
2
3
RawPassword (not stored)  |  Hashed   |     Salt
--------------------------------------------------------
letmein                       WEFLS...       WEFOJFOFO...

常用密码字典:

1
2
3
4
5
   Common Password
   --------------
   letmein
   12345
   ...

对于每个用户记录,循环常见密码并散列它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for each user in hacked_DB

    salt=users_salt
    hashed_pw = users_hashed_password

    for each common_password

        testhash = sha1(common_password + salt)
        if testhash = hashed_pw then
           //Match!  Users password = common_password
           //Lets visit the webpage and login now.
        end if

    next

next

我希望这能更好地说明我的观点。

假设有10000个常见密码和10000个用户记录,我们需要计算100000000个散列来发现尽可能多的用户密码。可能需要几个小时,但这不是真正的问题。

裂缝理论研究进展

我们假设我们是一个损坏的网络主机,可以访问一个包含sha1散列和salts的数据库,以及混合它们的算法。数据库有10000条用户记录。

这个站点声称能够使用GPU计算每秒2300000000个sha1哈希。(在现实世界中,情况可能会变慢,但现在我们将使用引用的数字)。

(((95^4)/2300000000)/2)*10000 = 177
seconds

给定95个可打印的ASCII字符的完整范围,最大长度为4个字符,除以计算速率(变量),再除以2(假设发现密码的平均时间平均需要排列的50%),对于10000个用户,计算出长度小于等于4的所有用户密码需要177秒。

让我们调整一下以适应现实。

(((36^7)/1000000000)/2)*10000 = 2 days

假设不区分大小写,密码长度<=7,只有字母数字字符,需要4天的时间来解决10000个用户记录,我已经将算法的速度减半,以反映开销和非理想情况。

重要的是要认识到这是一个线性蛮力攻击,所有的计算都是相互独立的,因此对于多个系统来说,这是一个完美的任务。(也就是说,可以轻松地设置两台计算机,从不同的终端运行攻击,这将使执行时间缩短一半)。

考虑到递归散列密码1000次,使此任务的计算成本更高:

(((36^7) / 1 000 000 000) / 2) * 1000
seconds = 10.8839117 hours

这表示最大长度为7个字母数字字符,对于一个用户,执行速度低于引用数字的一半。

递归散列1000次可以有效地阻止覆盖攻击,但是针对用户数据的目标攻击仍然很容易受到攻击。


它不能阻止字典攻击。

它所做的就是阻止那些设法从彩虹表中获取密码文件副本的人使用哈希表来确定密码是什么。

但最终,它可能会被残忍的强迫。这部分的答案是强迫用户不要使用字典中的单词作为密码(例如,至少需要一个数字或特殊字符)。

更新:

我本应该早点提到的,但有些(大多数?)密码系统对每个密码使用不同的salt,可能与密码本身一起存储。这使得一张彩虹桌毫无用处。这就是Unix Crypt库的工作原理,现代类Unix操作系统用新的哈希算法扩展了这个库。

我知道在较新版本的GNU Crypt中添加了对sha-256和sha-512的支持。


是的,您只需要3天时间就可以使用sha1(salt密码)。这就是为什么好的密码存储算法使用1000次迭代散列:您需要8年时间。


更准确地说,字典攻击(即尝试详尽列表中所有单词的攻击)并非"不可能",但却不切实际:每一位salt都将所需的存储量和计算量翻倍。

这与预先计算的字典攻击不同,比如涉及彩虹表的攻击,在彩虹表中,salt是否是秘密的并不重要。

示例:使用64位salt(即8字节),您需要在字典攻击中检查264个附加密码组合。用一本包含20万个单词的字典

200,000 * 264 = 3.69 * 1024

最坏情况下的测试-而不是20万个无盐测试。

使用salt的另一个好处是攻击者无法从字典中预先计算密码散列值。这只会占用太多的时间和/或空间。

更新

您的更新假定攻击者已经知道盐(或者已经偷了盐)。这当然是另一种情况。但攻击者仍然不可能使用预先计算的彩虹表。这里最重要的是散列函数的速度。为了使攻击不切实际,散列函数需要很慢。MD5或SHA在这里不是很好的候选者,因为它们设计得很快,哈希算法的更好候选者是河豚或它的一些变体。

更新2

关于密码散列安全问题的一个很好的读物(远远超出了最初的问题,但仍然很有趣):

Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes

文章的推论是:使用bcrypt(基于Blowfish)或eksblowfish创建的盐散列,它允许您使用可配置的设置时间来降低散列速度。


字典是一种按键索引值的结构。在预计算字典攻击的情况下,每个键都是一个散列,相应的值是导致散列的密码。有了预先计算好的字典,攻击者就可以"立即"查找密码,从而生成登录所需的哈希。

有了salt,存储字典所需的空间会快速增长&hellip;,因此尝试预计算密码字典很快就变得毫无意义。

最好的盐是从密码随机数生成器中随机选择的。八个字节是实际大小,超过16个字节没有任何作用。

Salt不仅仅是"让攻击者的工作更令人恼火",它还消除了所有类型的攻击,即使用预计算字典。

另一个元素是完全保护密码所必需的,那就是"加强密钥"。一轮sha-1还不够好:一个安全的密码散列算法的计算速度应该非常慢。

许多人使用pbkdf2,一个密钥派生函数,它将结果反馈给哈希函数数千次。"bcrypt"算法类似,使用缓慢的迭代密钥推导。

当散列操作非常缓慢时,预计算表对攻击者来说越来越可取。但是,适当的盐会破坏这种方法。

评论

下面是我对这个问题的评论。

如果没有salt,攻击者就不会使用"update 2"中演示的方法。他只需在一个预先计算的表中进行查找,并在O(1)或O(log n)时间(n是候选密码的数量)中获取密码。salt是防止这种情况发生的原因,并强制他使用"更新2"中所示的O(N)方法。

一旦减少到O(N)攻击,我们必须考虑每次尝试需要多长时间。密钥增强可能会导致循环中的每一次尝试都需要一整秒钟,这意味着在10万用户上测试10万密码所需的时间将从3天延长到3年&hellip;,而只有10万密码,则很可能在这段时间内破解零密码。

您必须考虑到攻击者将使用他所能使用的最快的工具,而不是PHP,因此数千次迭代(而不是100次)将是增强密钥的一个好参数。计算单个密码的散列值需要花费一秒钟的大部分时间。

密钥增强是标准密钥派生算法pbkdf1和pbkdf2的一部分,来自pkcs 5,这使得密码混淆算法非常好("派生密钥"是"hash")。

StackOverflow上的许多用户之所以引用本文,是因为它是对JeffAtwood关于彩虹表危险性的文章的回应。这不是我最喜欢的文章,但它确实更详细地讨论了这些概念。

当然,您假定攻击者拥有一切:salt、hash、用户名。假设攻击者是一名损坏的托管公司员工,他将用户表转储到myprettypony.com fansite上。他正试图恢复这些密码,因为他会回头看看你的小马粉丝是否在他们的花旗银行账户上使用了相同的密码。

有了一个精心设计的密码方案,这家伙将不可能恢复任何密码。


加盐的目的是防止攻击者的努力被浪费。

如果没有salt,就可以在世界上每个数据库的每个用户上使用一个预先计算的哈希密码条目表(例如,所有字母数字5个字符串的md5,易于在线查找)。

使用特定于站点的salt,攻击者必须自己计算表,然后才能在站点的所有用户上使用它。

对于每个用户的salt,攻击者必须分别为每个用户进行这项工作。

当然,这对于直接从字典中保护非常弱的密码没有多大作用,但它可以保护相当强的密码免受这种摊销。


另外-一个重要的点-使用特定于用户的salt防止检测到两个具有相同密码的用户-他们的哈希将匹配。这就是为什么很多次散列是散列(salt+username+password)

如果您试图对哈希保密,攻击者也无法验证哈希。

编辑-刚刚注意到要点是在上面的评论中提出的。


实施盐以防止彩虹表攻击。彩虹表是一个预先计算的哈希列表,这使得将哈希转换为短语更加简单。你需要明白,除非我们有一个现代的散列算法,否则用盐作为密码破解的现代预防措施是无效的。

所以,假设我们正在与sha1合作,利用最近使用该算法发现的漏洞,假设我们有一台计算机以每秒1000000个哈希的速度运行,需要530万年才能找到冲突,所以是的,php可以每秒工作300次,大的woop,这并不重要。我们盐的原因是,如果有人真的费心生成所有常见的字典短语,(2^160人,欢迎来到2007年时代的剥削)。

所以这里有一个实际的数据库,有两个我用来测试和管理的用户。

1
2
3
RegistrationTime        UserName        UserPass    
1280185359.365591       briang      a50b63e927b3aebfc20cd783e0fc5321b0e5e8b5
1281546174.065087       test        5872548f2abfef8cb729cac14bc979462798d023

事实上,盐渍方案是您的sha1(注册时间+用户名)。去吧,告诉我我的密码,这些是生产中的真实密码。你甚至可以坐在那里用php拼凑出单词表。疯狂吧。

我不是疯了,我只是知道这是安全的。为了好玩,测试的密码是test。江户十一〔一〕号

您只需要为这个用户生成一个与27662aee8eee1cb5ab4917b09bdba31d091ab732垂直的彩虹表。这意味着我实际上可以让我的密码不受单个彩虹表的影响,黑客需要为27662aee8eee1cb5ab4917b09dba31d091ab732生成一个完整的彩虹表进行测试,并再次为briang生成f3f735311217529f2e20468004a2aa5b3dee7f。回想一下530万年的历史。想想只存储2^80哈希的大小(超过20字节),这是不可能发生的。

不要将salting混淆为一种生成哈希的方法,这是一种阻止彩虹表转换所有用户密码的方法。在这种技术水平上是不可能的。


字典攻击背后的思想是,您获取一个散列值并找到密码,从中计算散列值,而不进行散列值计算。现在用盐密码做同样的事情-你不能。

不使用salt使密码搜索与在数据库中查找一样容易。添加salt会使攻击者对所有可能的密码执行哈希计算(即使对于dictionary attach,这也会显著增加攻击时间)。


简单地说,salting并不能防止hash受到攻击(bruteforce或dictionary),它只会使它变得更难;攻击者要么需要找到salting算法(如果正确实现,将使用更多的迭代),要么使用bruteforce算法,除非非常简单,否则这几乎是不可能的。盐渍也几乎完全放弃了彩虹表查找的选择…


最简单的说法是:在不使用salting的情况下,每个候选密码只需要哈希一次,就可以与"已知世界"(受损数据库的集合)中的每个用户核对,这些用户的密码是通过相同的算法哈希的。使用salting时,如果可能的salt值的数量实质上超过了"已知宇宙"中的用户数量,则每个候选密码都必须为要测试的每个用户单独散列。


盐使得彩虹表攻击更加困难,因为它使单个密码哈希更难破解。假设你有一个可怕的密码,只有数字1。彩虹桌攻击会立刻破解这个。

现在假设数据库中的每个密码都是由许多随机字符组成的长随机值组成的。现在,您糟糕的密码"1"以哈希1加上一组随机字符(salt)的形式存储在数据库中,因此在本例中,彩虹表需要具有类似于:1的哈希。

所以假设你的salt是安全和随机的,比如说()%isldghasklu(%%),黑客的彩虹表需要有一个1*()%isldghasklu(%%的条目。现在,即使在这个简单的密码上使用彩虹表也不再实用。