286

在对数据库存储密码进行哈希处理时,我总是使用正确的每个条目的盐字符串。根据我的需要,将盐存储在散列密码旁边的数据库中始终可以正常工作。

但是,有些人建议将盐与数据库分开存储。他们的论点是,如果数据库被入侵,攻击者仍然可以构建一个彩虹表,考虑到一个特定的盐字符串,以便一次破解一个帐户。如果这个账户有管理员权限,那么他可能甚至不需要破解任何其他人。

从安全角度来看,将盐存放在不同的地方是否值得?考虑在同一台机器上具有服务器代码和数据库的 Web 应用程序。如果盐存储在该机器上的平面文件中,那么如果数据库被破坏,盐文件也将被破坏。

有什么推荐的解决方案吗?

4

7 回答 7

287

彩虹表的关键在于它们是预先创建并大量分发以节省其他人的计算时间 - 动态生成彩虹表所需的时间与直接破解密码+盐组合所需的时间一样长(因为实际上,在生成彩虹表时所做的是预先运行计算以强制哈希),因此通过知道盐某人可以“生成彩虹表”的论点是虚假的。

只要将盐存储在单独的文件中,只要它们是基于每个用户的,就没有真正的意义-盐的目的只是为了使彩虹表无法破坏数据库中的每个密码。

于 2009-08-02T21:31:38.553 回答
44

我将提供一个稍微不同的看法。

我总是将盐与加盐密码哈希混合在一起存储。

例如,我会将盐的前半部分放在密码的盐渍散列之前,将盐的后半部分放在密码的盐渍散列之后。应用程序知道这种设计,因此可以获取此数据,并获得盐和盐渍密码哈希。

我采用这种方法的理由:

如果密码/哈希数据被泄露并落入攻击者手中,攻击者将无法通过查看数据知道盐是什么。这样,攻击者实际上无法执行暴力攻击来获得与哈希匹配的密码,因为他不知道哈希的开头,也无法知道数据的哪些部分是盐的一部分,或者加盐密码哈希的一部分(除非他确实知道您的应用程序的身份验证逻辑)。

如果加盐密码散列按原样存储,则可以执行蛮力攻击以获得一个密码,该密码在加盐和散列时产生与加盐密码散列相同的数据。

但是,例如,即使加盐密码哈希按原样存储,但预先附加了单个随机字节,只要攻击者不知道要丢弃第一个字节,这也会增加难度的攻击。当您的应用程序用于验证您的用户时,您的应用程序会知道丢弃数据的第一个字节。

这个结论..

1) 永远不要以准确的形式存储您的身份验证应用程序使用的数据。

2) 如果可能,将您的身份验证逻辑保密以增加安全性。

更进一步。。

如果您不能对应用程序的身份验证逻辑保密 - 很多人都知道您的数据是如何存储在数据库中的。并且假设您决定将加盐密码散列与盐混合在一起存储,其中一些盐在加盐密码散列之前,其余的加盐将其附加。

生成随机盐时,您还可以随机决定在加盐密码哈希之前/之后存储的盐的比例。

例如,您生成一个 512 字节的随机盐。您将盐附加到您的密码中,并获取您的加盐密码的 SHA-512 哈希。您还生成一个随机整数 200。然后存储 salt 的前 200 个字节,然后是 salted-password 哈希,然后是 salt 的其余部分。

在验证用户的密码输入时,您的应用程序将传递字符串,并假设数据的前 1 个字节是 salt 的前 1 个字节,然后是 salted-hash。此通行证将失败。应用程序将继续使用数据的前 2 个字节作为 salt 的前 2 个字节,并重复使用前 200 个字节作为 salt 的前 200 个字节后找到肯定的结果。如果密码错误,应用程序将继续尝试所有排列,直到找不到。

这种方法的优点:

提高安全性 - 即使您的身份验证逻辑是已知的,确切的逻辑在编译时也是未知的。即使知道确切的逻辑,也几乎不可能执行暴力攻击。增加盐的长度将进一步提高安全性。

这种方法的缺点:

由于在运行时推断出确切的逻辑,因此这种方法非常占用 CPU。salt 的长度越长,这种方法的 CPU 密集度就越高。

验证不正确的密码将涉及最高的 CPU 成本。这对合法请求可能会适得其反,但会增加对攻击者的安全性。

这种方法可以以多种方式实现,并且可以通过使用可变宽度的盐和/或加盐密码散列变得更加安全。

于 2013-05-12T13:16:03.570 回答
27

通常,它们被添加到散列并存储在同一字段中。

无需单独存储它们 - 关键是为每个密码使用随机盐,以便单个彩虹表不能用于您的整个密码哈希集。使用随机盐,攻击者必须分别暴力破解每个散列(或为所有可能的盐计算彩虹表——工作量要大得多)。

如果您有一个更安全的存储位置,那么将哈希存储在那里是有意义的。

于 2009-08-02T21:42:26.983 回答
5

基于 William Penberthy 的《开发 ASP.NET MVC 4 Web 应用程序》一书:

  1. 要访问存储在单独数据库中的盐,黑客需要破解两个不同的数据库才能访问盐和加盐密码。将它们与密码存储在同一个表中,甚至是同一个数据库的另一个表中,这意味着当黑客获得对数据库的访问权时,他们将可以访问盐和密码哈希。由于安全性包括使入侵系统的过程过于昂贵或耗时而不值得,因此将黑客必须获得的访问量增加一倍应该会使系统更加安全。
  2. 易于使用是将盐保存在与散列密码相同的数据库中的主要原因。您不必确保两个数据库始终同时可用,并且始终保持同步。如果每个用户都有一个随机的 salt,那么使用 salt 的优势是最小的,因为虽然它可能更容易发现个人的密码,但破解整个系统密码所需的力量会很大。在这个级别的讨论中,这就是真正的期望:保护密码。如果黑客获得了数据库的副本,则您的应用程序数据已经受到损害。在这一点上,问题是降低用户的风险,因为共享密码的可能性。
  3. 维护两个独立的链接数据库的需求非常广泛。诚然,它增加了安全感,但它提供的唯一优势是它可以保护密码,即单个数据元素。如果数据库中的每个字段都单独加密,并且使用相同的盐,那么将其与数据分开存储会更有意义,因为系统的基本安全性得到了增强。
于 2014-12-06T19:26:16.703 回答
1

盐的目的是使所有彩虹表无用,并需要制作一组新的彩虹表。 猜一个字符串的时间与制作一张彩虹表的时间一样长。 例如“密码”的 SHA-256 哈希是5e88 4898 da28 0471 51d0 e56f 8dc6 2927 7360 3d0d 6aab bdd6 2a11 ef72 1d15 42d8. 添加盐后,例如“badpassword”,要散列的新字符串是“passwordbadpassword”,由于雪崩效应,它会极大地改变输出,到457b f8b5 37f1 802e f9c8 2e46 b8d3 f8b5 721b 7cbb d485 f0bb e523 bfbe 73e6 58d6.

通常盐只是与密码存储在同一个数据库中,因为如果一个数据库被黑客入侵,另一个数据库很可能也会被黑客入侵。

于 2019-02-24T23:00:43.857 回答
0

之所以使用盐渍,是为了防止彩虹表附着。恶意用户以某种方式到达数据库并看到散列密码,获取最常见密码表,找到它们的散列值并在表中查找密码。

所以当用户发送密码时,我们将随机生成的盐添加到密码中。

 userPassword + salt

我们将它传递给我们的散列算法。

 hash(userPassword+salt)

由于盐是随机生成的,userPassword+salt因此成为随机值,绝对不是最常用的密码之一。所以恶意用户不会通过检查彩虹表来弄清楚使用了什么密码。

现在盐值被添加到哈希值之前,因为在用户登录时再次使用它来比较传递的凭据和保存的凭据。

 hash(userPassword+salt)=ashdjdaskhfjdkhfjdashadslkhfdsdh

这就是密码存储在数据库中的方式:ashdjdaskhfjdkhfjdashadslkhfdsdh.salt

现在,如果恶意用户看到这一点,他可以找出密码,但这将花费大量时间。因为每个密码都会得到不同的盐。让我们恶意拥有 5000 个常用密码及其哈希表。

一件重要的事情,恶意用户不只有一张表。因为有太多不同的算法,所以恶意用户对于每个算法都会有5000个密码的哈希值。

现在对于每个密码,假设他从第一个用户的密码开始,他会将该盐添加到 5000 个常用密码中,并为每个不同的算法创建一个新的彩虹表,以便仅找到一个密码。然后对于第二个用户的密码,他会看到不同的盐,他会计算新的彩虹表。甚至不能保证,用户的密码将在那些常见的密码列表中。

于 2021-03-17T04:41:54.477 回答
0

如果您使用使用固定大小字符串作为 salt 的库(或制作自己的库),那么您可以将 salt 和哈希密码存储在同一字段中。然后,您将拆分存储的值以检索盐和散列密码以验证输入。

使用 10 个字符的盐和 40 个字符的固定哈希大小,这看起来像这样:

salt = "california"
passwd = "wp8tJ4Pr"

stored_passwd = salt + hash(passwd + salt)

salt = substr(stored_passwd, 0, 10)
hashed_passwd = substr(stored_passwd, 10, 40)

if hash(user_input + salt) == hashed_passwd:
    print "password is verified"

由于盐的全部目的是防止使用预先计算的表(例如彩虹表)进行密码攻击,因此将盐与散列密码一起存储实际上是无害的。

于 2021-05-17T20:00:54.260 回答