在散列数据库存储的密码时,我总是使用适当的每项salt字符串。根据我的需要,将salt存储在哈希密码旁边的数据库中总是可以正常工作的。
但是,有些人建议将盐与数据库分开存储。他们的论点是,如果数据库受到破坏,攻击者仍然可以在考虑特定salt字符串的情况下构建彩虹表,以便一次破解一个帐户。如果这个帐户有管理员特权,那么他甚至不需要破解任何其他帐户。
从安全的角度来看,在不同的地方储存盐是否值得?考虑在同一台计算机上使用服务器代码和数据库的Web应用程序。如果盐存储在那台机器上的平面文件中,那么很有可能数据库受到破坏,盐文件也会受到破坏。
对此有什么建议的解决方案吗?
- 如果有一个地方可以存储攻击者无法获取的盐,那么您也应该将密码存储在那里。但是为什么不为每个密码使用不同的salt呢?
- 他对每个密码使用不同的salt,jrockway。
- 你的盐有多大?你的盐应该足够大(32比特?)实际上,没有可能预先计算出彩虹表。
- @Emddudley这些天来,我一直习惯用64位整数作为盐,但是没有理由我不能再做了。
- 我还想把这个数据库管理员Q放入dba.stackexchange.com/questions/7492/…
- 这里是pwdtk的作者sourceforge.net/projects/pwdknet,老实说,我不会担心,我只会将salt存储在与密码相同的数据库中。您应该始终假设攻击者知道salt,所以您的重点应该是使用大型加密随机salt并执行足够的密钥扩展(pbkdf2中的迭代),这样就不可能为一个已知salt创建一个彩虹表。老实说,您试图通过将salt放在其他地方来实现的是"默默无闻的安全性",当您看到诸如其他服务器等可能出现故障的情况时,通常没有任何好处。
- @弗里多,光盐是不够的。用胡椒粉。
- @塞希兹涅茨,如果我们谈论的是辣椒,那就不安全了。密码是安全的。
- @Pacerier Pepper本质上是一种全球性的食盐,这个问题涉及到隐藏食盐,你的密码方案应该足够好,即使他们有食盐,制作彩虹桌也需要很长时间。然而,这样做可能有一个小的好处,以防止白痴用户使用密码作为他们的密码或其他东西。
彩虹表的要点是,它们是预先创建的,并集中分布,以节省其他人的计算时间-在运行中生成彩虹表所需的时间与直接破解密码+盐组合所需的时间相同(因为生成彩虹表时所做的工作实际上是预运行计算因此,通过知道盐可以"生成彩虹表"的说法是错误的。
在单独的文件中存储盐没有真正的意义,只要它们是基于每个用户的基础上-盐的意义只是简单地使之成为一个彩虹表不能打破数据库中的每个密码。
- 同意。您通过单独存储salt来保护的威胁模型是一个用户,他可以以某种方式通过恶意手段访问db中的salt,但不能访问(db中的)hash。那个人会提前开始计算一个彩虹表,假设他以后能找到哈希值。这不是不可能的,但也不值得为防御这一单一攻击途径而付出工程努力。
- 好帖子,我也在想同样的事情。我从来没有想过每个用户都有一种盐,我认为一种盐对所有用户都有效。存储为应用服务器加载的XML文件的salt怎么样?或者可能以某种方式硬编码到servlet中?
- @如果您没有为每个用户提供单独的盐,那么Jigzat-Salting是没有意义的。salt s的要点是使每个用户密码的哈希值中断成为一个单独的任务;如果所有密码的salt都相同,那么情况就不同了。
- @汤里特不是唯一的例子。您假设所有密码都很复杂。一些攻击者可能会获取salt和hash,只检查10000个最常见的密码。这样他们就能得到相当数量的人。但是,如果他们不能访问salt,这就类似于用户拥有更长的更安全的密码。现在,在密码数据库被盗的情况下,SALT数据库的安全性有多大可能有待讨论,但这是一个单独的问题。
- @Amber,我相信Tomritter是对的。单独存储salt意味着强制攻击者使用蛮力攻击与更简单的字典攻击之间的区别。如果你知道盐,你可以在运行磨坊字典攻击时附加它。如果您可以100%保护您的salt,您可以简单地使用相同的salt并强制攻击者暴力强制所有内容(即使对于使用"密码"作为密码的用户)。但是你能保护你的盐吗?可能不会。因此,还可以通过将其存储在哈希旁边并强制实施更强的密码规则来减少故障点。
- @如果您没有为每个用户提供单独的盐,那么Jigzat Salting并不是没有意义的,除非您的盐已经有一个广泛分布的彩虹表,并且每个人都知道要尝试哪一个。更可能的情况是,如果他们得到了你的盐,他们将不得不重新生成另一张桌子,只为你的东西,它会减慢他们,无论如何。当然,每个人都有不同的选择仍然是不可取的。
- "重新生成一个单独的表"的工作与强制使用单个密码的工作相同。盐的意义并不是防止暴力地强制单一密码,所以共享盐实际上并没有多少有效的收益。如果你打算这样做的话,你最好不要使用盐,而只是以与其他人稍有不同的方式散列密码。(但不要使用单独的盐,因为这是真正安全的方法。)
- @Amber,我想你可能错过了什么。当你知道盐的数量,你必须暴力的密码是大大减少了,对吗?因为您已经知道大部分密码(假设salt比用户的密码长),所以彩虹表的大小要小得多。如果盐是隐藏的,那么在我看来,这是不可行的,因为你必须创建一个彩虹表,包含所有可能的盐,假设你不能得到隐藏的盐。
- 我要补充的是,一个更合理的策略肯定是使用更强大的哈希函数,而不是依靠隐藏的盐来保护您免受暴力攻击,所以结果是一样的。
- Reddit的联合创始人在Udacity上开设了WebDevelopment课程,这也是我第一次学习它,我当时也很理解它。
- @由于这两种方法在任何合理的时间段内都不太可能成功,我认为这是纯粹的学术论证,而且在实践中,隐藏盐没有任何实际的好处。
我将提供一个略有不同的看法。
我总是把盐和盐密码散列一起存储。
例如,我将把盐的前半部分放在密码的盐散列之前,将盐的后半部分放在密码的盐散列之后。应用程序知道这种设计,因此可以获取这些数据,并获得salt和salt密码散列。
我采用这种方法的理由是:
如果密码/散列数据被破坏并落入攻击者手中,攻击者将不知道SALT从数据中是什么。这样一来,攻击者实际上无法执行暴力攻击以获取与哈希匹配的密码,因为他不知道要开始的哈希,也无法知道哪些数据部分是salt的一部分,或者是salt密码哈希的一部分(除非他知道应用程序的身份验证逻辑)。
如果salted密码散列按原样存储,则可以执行暴力攻击以获取一个密码,当salted和hashed产生与salted密码散列相同的数据时,该密码将被执行。
但是,例如,即使salted密码散列按原样存储,但预先挂起一个随机字节,只要攻击者不知道第一个字节将被丢弃,这也会增加攻击的难度。当用于验证用户身份时,应用程序将知道丢弃数据的第一个字节。
结论……
1)永远不要将您的身份验证应用程序使用的数据以其准确的形式存储。
2)如果可能,为增加安全性,请对您的身份验证逻辑保密。
再往前走一步……
如果您不能对应用程序的身份验证逻辑保密,那么许多人都知道您的数据是如何存储在数据库中的。假设您已经决定将盐密码散列与盐混合存储在一起,其中一些盐在盐密码散列前面,其余的盐在散列后面。
当生成随机的salt时,您还可以随机决定在salt密码哈希之前/之后要存储的盐的比例。
例如,生成512字节的随机salt。您将salt附加到您的密码,并获得您的salt密码的sha-512哈希。您还可以生成一个随机整数200。然后存储前200个字节的salt,然后存储salt密码散列,最后存储其余的salt。
当验证用户的密码输入时,应用程序将传递字符串,并假设数据的前1个字节是salt的前1个字节,然后是salt散列。这个通行证将失败。应用程序将继续使用数据的前2个字节作为salt的前2个字节,并重复,直到在使用前200个字节作为salt的前200个字节后找到正结果。如果密码错误,应用程序将继续尝试所有排列,直到找不到为止。
这种方法的优点是:
提高了安全性—即使您的身份验证逻辑已知,但在编译时确切的逻辑是未知的。即使知道确切的逻辑,也几乎不可能执行蛮力攻击。增加盐的长度将进一步提高安全性。
这种方法的缺点是:
由于确切的逻辑是在运行时推断出来的,所以这种方法占用大量CPU。盐的长度越长,这种方法的CPU强度就越大。
验证不正确的密码将涉及最高的CPU成本。这可能会对合法请求产生反作用,但会提高对攻击者的安全性。
这种方法可以以各种方式实现,并且可以通过使用可变宽度的salts和/或salted密码散列使其更加安全。
- 使用这种方法,您只是在散列过程中添加了一个秘密(应用salt的算法)。这个秘密你可以更容易地添加一个胡椒盐额外的盐,我试图在我的教程中指出这一点。像bcrypt这样的现代散列函数将自己应用salt,在每次迭代中使用原始salt,因此无论如何您都无法控制这一点。
- @Martinstoeckli虽然您认为bcrypt自己应用盐是正确的,但是盐+散列的存储取决于您作为开发人员。因此,您可以很容易地将胡椒粉添加到salt+hash并将其持久化到数据库中。然后,在随后的检索中,从数据库中读取该值,去掉胡椒值,并将剩余值传递给bcrypt。
- @皮特泰德-这会否定胡椒的优点。Pepper添加了一个服务器端的秘密,并且只要它保持秘密(与salt相反),就可以工作。典型的攻击是SQL注入,当有人访问数据库而不是代码时,存储在数据库中的胡椒就没有用了。大多数bcrypt实现都会自动将salt添加到生成的哈希值中,因此该值已经包含salt、成本因素、算法和哈希。此字符串可以存储在一个长度为60个字符的字段中。
- 另外,当使用"关键强化"功能(如bcrypt)时,您无法控制盐的使用。但是,如果您想使用胡椒,只需将胡椒添加到盐中,并将其作为"胡椒盐"来代替哈希函数的"salt"输入。然后,"pepper"是一个合适的数据块,它不是存储在数据库中,而是嵌入在身份验证代码中,或者存储在另一个安全位置。我从一般的角度来探讨这个问题,使用sha-512作为示例函数,但是bcrypt等也可以以类似的方式使用。
- @易卜拉欣——实际上,胡椒粉应该和密码结合在一起,而不是和盐结合在一起。否则,返回结果散列值中的盐的函数将返回组合的盐辣椒。你可能想到了一些你必须分开存放盐的功能。
- @martinstoeckli-是的,实际的实现取决于您使用的散列函数。显然,在实现身份验证逻辑时,需要考虑哈希函数的参数和输出。最后,pepper只是散列函数中引入的另一个变量,它与salt和hash存储在不同的位置。
- 这不会以任何方式增加安全性。如果我可以下载您的数据库,我可以下载/反向工程您的应用程序代码。黑暗中的安全?不是真的。
- @虽然从技术上讲这是可能的,但如果您有可用的数据,从数据库中提取信息实际上是不可能的,这就是这种方法的全部意义——降低了攻击的实用性。
- 我不同意,所以我投了反对票。这不会以任何方式增加安全性。
通常情况下,它们被预先添加到散列并存储在同一个字段中。
不需要单独存储它们——重点是对每个密码使用随机的salt,这样就不能对整个密码哈希集使用一个彩虹表。对于随机的盐类,攻击者必须对每个散列值分别强制执行(或者为所有可能的盐类计算一个彩虹表——要做的工作要多得多)。
如果您有一个更安全的存储位置,那么只将散列存储在那里是有意义的。
- 但是,如果所有散列密码都被泄露了,包括它们的匹配salt,会发生什么呢?这不是没有安全感吗?
- @但是如果你想知道"密码",你仍然需要为每一种盐构建一个彩虹表,除非有些盐是相同的。
基于William Penberthy的《开发ASP.NET MVC 4 Web应用程序》一书:
要访问存储在单独数据库中的盐,需要黑客攻击两个不同的数据库可以访问salt和salted密码。将它们存储在与密码相同的表,甚至同一数据库的另一个表都将这意味着当黑客访问数据库时,他们将可以访问salt和密码哈希。因为安全包括黑客攻击的过程进入系统的成本太高或时间太长,不值得,将金额翻倍如果让系统更安全,黑客必须获得的访问权限。
易用性是保持盐与哈希密码。您不必确保两个数据库始终可用同时,始终保持同步。如果每个用户都有一个随机的盐,因为尽管它可能会发现一个人的密码更容易,破解密码所需的力度系统总体将很高。在这一级别的讨论中,这正是我们所期望的是:保护密码。如果黑客获得了数据库的副本,您的应用程序数据已被破坏。此时,问题是要减轻用户的共享密码的潜在风险。
维护两个独立的链接数据库的要求非常广泛。当然,它增加了对安全的感知,但它所给予的唯一好处是它可以保护一个密码,一个数据元素。如果数据库中的每个字段都是单独的加密的,同样的盐也用于此目的,存储它会更有意义。与数据分开,因为系统的基本安全性得到了增强。
盐的作用是使所有彩虹桌都变得无用,并要求制作一套新的彩虹桌。猜一根绳子就像做一张彩虹桌一样长。例如,"password"的sha-256散列是5e88 4898 da28 0471 51d0 e56f 8dc6 2927 7360 3d0d 6aab bdd6 2a11 ef72 1d15 42d8。添加salt后,例如"badpassword",要散列的新字符串是"passwordbadpassword",由于雪崩效应,它会将输出显著更改为457b f8b5 37f1 802e f9c8 2e46 b8d3 f8b5 721b 7cbb d485 f0bb e523 bfbe 73e6 58d6。
通常,salt与密码存储在同一个数据库中,也因为如果一个数据库被黑客攻击,另一个数据库也很可能被黑客攻击。