为什么盐会使字典攻击“不可能”?

可能重复:
需要一些帮助了解密码盐

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

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

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

在你存储的数据库中:

 username | hashed_password | salt 

每一次我见过的盐都会在密码的末尾添加salt,或者开始:

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

因此,一个值得他的盐的黑客的字典攻击(哈哈)将简单地运行每个关键字对存储的盐在上面列出的常见组合。

上面描述的实现只是为黑客添加了另一个步骤,而没有真正解决潜在的问题? 有什么替代方法来解决这个问题,或者我误解了这个问题?

我唯一能想到的就是有一个秘密的混合algorithm,它将salt和password绑定在一起,或者将其他用户字段添加到散列过程中,这意味着黑客必须有权访问数据库,他们的字典攻击certificate富有成效。 (更新,正如在评论中指出的那样,最好假定黑客可以访问你的所有信息,所以这可能不是最好的)。

让我举一个例子,说明我build议黑客会用密码和哈希列表破解用户数据库:

我们被黑客窃取的数据

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

常用密码字典:

  Common Password -------------- letmein 12345 ... 

对于每个用户logging,循环使用通用密码并对其进行散列:

 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 

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

假设有10,000个通用密码和10,000个用户logging,我们需要计算1万个哈希以发现尽可能多的用户密码。 这可能需要几个小时,但这不是一个真正的问题。

破解理论更新

我们将假定我们是一个损坏的networking主机,可以访问SHA1哈希和盐的数据库,以及您的algorithm来混合它们。 该数据库有10,000个用户logging。

这个网站声称能够使用GPU每秒计算2,300,000,000个SHA1哈希值。 (在现实世界中情况可能会比较慢,但现在我们将使用这个引用的数字)。

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

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

让我们调整一下现实主义。

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

假设不区分大小写,密码长度小于等于7,只有字母数字字符,需要4天的时间才能解决10,000条用户logging,而我已经将algorithm的速度减半,以反映开销和非理想情况。

认识到这是线性蛮力攻击是非常重要的,所有的计算都是相互独立的,因此对于多个系统来说这是一个完美的任务。 (IE容易设置2台计算机从不同的端点执行攻击,执行时间只有一半)。

鉴于recursion散列密码1,000次的情况,使得这个任务在计算上更加昂贵:

(((36 ^ 7)/ 1 000 000 000)/ 2)×1000秒= 10.8839117小时

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

recursion散列1000次有效地阻止了一次全面攻击,但针对用户数据的攻击仍然是脆弱的。

是的,你只需要3天的时间(盐|密码)。 这就是为什么好的密码存储algorithm使用1000次迭代哈希:你将需要8年。

它不会停止字典攻击。

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

不过最终它可能会被蛮荒的。 该部分的答案是强制用户不要使用字典中的单词作为密码(例如至less一个数字或特殊字符的最低要求)。

更新

我应该早些提到这一点,但是一些(大多数?)密码系统对每个密码使用不同的盐,可能与密码本身一起存储。 这使得一个单一的彩虹桌无用。 这就是UNIX 密码库的工作方式,而现代的类UNIX操作系统已经使用新的哈希algorithm扩展了这个库。

我知道在新版本的GNU crypt中增加了对SHA-256和SHA-512的支持。

更确切地说, 字典式的攻击 ,即一个穷尽列表中的所有单词都被尝试的攻击,并不是“不可能的”,但是不切实际 : 每一点盐都会使所需的存储量和计算量翻倍 。

这与预先计算的字典式攻击不同,例如涉及彩虹表的攻击,不pipe盐是否秘密。

例如:对于64位盐(即8个字节),您需要在字典攻击中检查2 64个额外的密码组合。 用一个包含20万字的字典,你将不得不作出

200,000 * 2 64 = 3.69 * 10 24

在最坏的情况下testing – 而不是没有盐的20万次testing。

使用salt的另一个好处是攻击者不能从他的字典中预先计算密码哈希值。 这只会花费太多的时间和/或空间。

更新

您的更新假定攻击者已经知道盐(或已经被盗)。 这当然是不同的情况。 攻击者仍然不可能使用预先计算的彩虹表。 这里重要的是哈希函数的速度。 为了使攻击不切实际,散列函数需要缓慢。 MD5或SHA在这里不是很好的候选者,因为它们被devise成快速,更好的散列algorithm候选者是Blowfish或者它的一些变体。

更新2

关于保护你的密码散列问题的一个很好的阅读(远远超出原来的问题,但仍然有趣):

足够的彩虹表:你需要知道的安全密码scheme

文章的推论:使用由bcrypt (基于Blowfish)或Eksblowfish创build的咸味散列,允许您使用可configuration的安装时间使散列变慢。

字典是一种通过键索引值的结构。 在预先计算的字典攻击的情况下,每个密钥都是一个散列,相应的值是导致散列的密码。 用手中的预先计算的字典,攻击者可以“立即”查找密码,这将产生必要的散列login。

用盐,存储字典所需的空间迅速增长…如此之快,试图预先计算密码字典很快变得毫无意义。

最好的盐是从密码随机数发生器中随机select的。 八个字节是实际的大小,超过16个字节没有用处。


盐不仅仅是“让攻击者的工作更加恼人”。 它消除了整个类别的攻击 – 使用预先计算的字典。

另一个要素是完全保护密码,这是“加强密码”。 一轮SHA-1是不够好的:一个安全的密码哈希algorithm应该是非常慢的计算。

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

当散列操作非常缓慢时,预先计算的表对于攻击者来说变得越来越需要。 但适当的盐打败了这种做法。


注释

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


如果没有盐,攻击者将不会使用“更新2”中显示的方法。 他只需在预先计算的表格中进行查找,然后在O(1)或O(log n)时间(n是候选密码的数量)中获取密码。 盐是什么阻止,迫使他使用“更新2”中所示的O(n)方法。

一旦减less到O(n)攻击,我们必须考虑每个尝试需要多长时间。 加强密钥可能会导致循环中的每一次尝试都花费一整秒,这意味着在10k用户上testing10k密码所需的时间将从3天延长到3 ,而只有10k密码,您可能会破解零密码在那个时候。

您必须考虑到攻击者会使用他能够使用的最快的工具,而不是PHP,因此数千次迭代(而不是100次)对于加强密钥是一个很好的参数。 计算单个密码的散列值需要花费大量的时间。

encryption是来自PKCS#5的标准密钥派生algorithmPBKDF1和PBKDF2的一部分,它们使得密码模糊化algorithm(“派生密钥”是“散列”)变得很好。

StackOverflow上的很多用户都提到了这篇文章,因为这是对Jeff Atwood关于彩虹桌危险的文章的回应。 这不是我最喜欢的文章,但它更详细地讨论这些概念。


当然,你认为攻击者拥有一切:salt,hash,用户名。 假设攻击者是一个腐败的托pipe公司员工,将用户表转储到myprettypony.com粉丝团。 他正在尝试恢复这些密码,因为他将转身,看看你的小马迷在他们的citibank.com帐户上使用了相同的密码。

有了一个精心devise的密码scheme,这个家伙将不可能恢复任何密码。

腌制的目的是防止攻击者努力的摊销。

在没有盐的情况下,可以在世界上每个数据库中的每个用户上使用预先计算的哈希密码条目的单个表格(例如,所有字母数字5string的MD5,在线查找)。

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

随着每个用户的盐,攻击者必须分别为每个用户花费这个努力。

当然,这并没有太大的作用来保护字典中的真正弱密码,但是它保护了合理的密码。

此外,还有一点更重要的是,使用特定于用户的salt可以防止使用SAME密码的两个用户的检测 – 它们的哈希将匹配。 这就是为什么很多时候哈希是散列(盐+用户名+密码)

如果您尝试保持哈希密码,攻击者也无法validation哈希值。

编辑 – 只是注意到主要点是在上面的评论。

盐被实施防止彩虹桌攻击。 彩虹表是一个预先计算的散列列表,这使得将散列翻译成它的短语更为简单。 你需要明白,除非我们有一个现代的哈希algorithm,否则腌制作为现代预防破解密码是无效的。

因此,我们可以说我们正在使用SHA1,利用最近在这个algorithm中发现的漏洞,并且假设我们有一台计算机以每秒100万次散列运行, 那么需要530万年才能find一个冲突 ,所以是的PHP可以工作300秒,大杂烩,其实并不重要。 我们之所以盐,是因为如果有人打算生成所有常见的词典短语,(2 ^ 160人,欢迎到2007年的时代的利用)。

所以这是一个实际的数据库,有两个用户用于testing和pipe理目的。

 RegistrationTime UserName UserPass 1280185359.365591 briang a50b63e927b3aebfc20cd783e0fc5321b0e5e8b5 1281546174.065087 test 5872548f2abfef8cb729cac14bc979462798d023 

实际上,saltingscheme是你的sha1(注册时间+用户名)。 继续,告诉我我的密码,这些是生产中的真正的密码。 你甚至可以坐在那里,并在PHP中散列出一个单词列表。 去野外。

我不是疯了,我只知道这是安全的。 为了乐趣,testing的密码是testsha1(sha1(1281546174.065087 + test) + test) = 5872548f2abfef8cb729cac14bc979462798d023

您需要为此用户生成一个完整的彩虹表,并使用27662aee8eee1cb5ab4917b09bdba31d091ab732 。 这意味着我实际上可以允许我的密码不被所有的彩虹表妥协,黑客需要生成一个完整的彩虹表以供testing,并且再次为briang提供f3f7735311217529f2e020468004a2aa5b3dee7f。 回想一下530万年的所有哈希。 想想只存储2 ^ 80哈希值(远远超过20 兆字节 )的大小,这是不会发生的。

不要混淆salting作为一种你无法解码的散列方法,这是防止彩虹表翻译所有用户密码的一种手段。 这是在这个技术水平上的可能性。

字典攻击背后的想法是,你拿一个散列,并find密码,从这个散列计算, 没有散列计算。 现在做同样的腌制密码 – 你不能。

不使用盐使密码search就像在数据库中查找一样简单。 join一个盐让攻击者对所有可能的密码执行哈希计算(即使是字典附加,这也会显着增加攻击时间)。

最简单的说法是:不用腌制,只需要对每个候选密码进行一次散列检查,以对照每个用户,在“已知的宇宙”(受损数据库的集合)中的任何地方进行检查,其密码通过相同的algorithm散列。 在使用盐析的情况下,如果可能盐值的数量远远超过“已知宇宙”中的用户数量,则每个候选密码必须分别针对每个将被testing的用户进行散列。

简单地说,腌制并不能防止散列攻击(暴力或字典),这只会使它更难; 攻击者或者需要find腌制algorithm(如果正确实施将会使用更多的迭代)或者强制algorithm,除非很简单,几乎是不可能的。 盐渍也几乎完全舍弃彩虹表查找的选项…

盐使得彩虹桌攻击变得更加困难,因为它使得一个单一的密码哈希更难以破解。 想象一下,你有一个可怕的密码,只有1号。彩虹表攻击会立即破解。

现在想象一下,db中的每个密码都被一个长随机值的很多随机字符所淹没。 现在你的糟糕的密码“1”被存储在数据库中,作为散列1加上一堆随机字符(盐),所以在这个例子中,彩虹表需要有如下的散列:1。

所以假设你的salt是安全的和随机的,比如()%ISLDGHASKLU( %#%#),黑客的彩虹表就需要有1 *()%ISLDGHASKLU(*%#%#就连这个简单的密码也不再实用。