ChatGPT解决这个技术问题 Extra ChatGPT

你在哪里存放你的盐串?

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

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

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

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

如果有一个地方可以存储攻击者无法获取的盐,那么你也应该在那里存储密码。但是为什么不对每个密码使用不同的盐呢?
他为每个密码使用不同的盐,jrockway。
你的盐有多大?您的盐应该足够大(32 位?),几乎不可能为它预先计算彩虹表。
@emddudley 这些天我一直习惯使用 64 位整数作为盐,但我没有理由不能让它们更长。
PWDTK 的作者在这里 sourceforge.net/projects/pwdtknet ,老实说,我不会担心,我只会将盐存储在与密码相同的数据库中。您应该始终假设攻击者知道盐,因此您的重点应该是使用 LARGE CRYPTO-RANDOM salt 并执行足够的密钥拉伸(PBKDF2 中的迭代),以便为一种已知的盐制作一个彩虹表是不可行的。老实说,您试图通过将盐放在其他地方来实现的是“默默无闻的安全性”,并且当您看到另一台服务器可能出现故障时,通常没有任何好处。

A
Amber

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

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


同意。您通过单独存储盐来保护的威胁模型是一个用户,该用户可以通过邪恶的方式以某种方式访问数据库中的盐,但不能访问哈希(在数据库中)。并且那个人将提前开始计算彩虹表,假设他以后能够找到散列。并非不可能,但也不值得为防御这种单一的攻击途径而付出工程努力。
不错的帖子,我也想知道同样的事情。我从没想过每个用户一个盐,我认为一种盐对所有用户都有效。存储为由应用服务器加载的 XML 文件的盐呢?或者可能以某种方式硬编码到 servlet 中?
@Jigzat - 如果您没有为每个用户提供单独的盐,那么加盐是没有意义的。 salts 的重点是使破解哈希成为每个用户密码的单独任务;如果所有的盐都相同,那么情况并非如此。
@TomRitter 这不是唯一的情况。您假设所有密码都很复杂。一些攻击者可能会使用 salt 和 hash 并仅检查 10,000 个最常见的密码。这样他们就会得到相当数量的人。但是,如果他们无法访问盐,这类似于用户拥有更长更安全的密码。现在,在密码数据库被盗的情况下,盐数据库保持安全的可能性有多大还有待商榷,但这是一个单独的问题。
@Amber,我相信 TomRitter 是正确的。单独存储盐意味着强制攻击者使用暴力攻击与更简单的字典攻击之间的区别。如果您知道盐,您可以在运行磨坊字典攻击期间附加它。如果您可以 100% 保护您的盐,您可以简单地使用相同的盐并强制攻击者暴力破解所有内容(即使对于使用“密码”作为密码的用户)。但是你能捍卫你的盐吗……可能不会。因此,不妨通过将其存储在哈希旁边并强制执行更强大的密码规则来减少故障点。
I
Ibraheem

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

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

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

我采用这种方法的理由:

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

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

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

这个结论..

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 成本。这对合法请求可能会适得其反,但会增加对攻击者的安全性。

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


使用您的方法,您只是在散列过程(应用盐的算法)中添加了一个秘密。您可以通过在盐中额外添加 胡椒 来更轻松地添加这个秘密,我试图在我的 tutorial 中指出这一点。像 BCrypt 这样的现代散列函数将自行应用盐,在每次迭代中使用原始盐,所以无论如何你都无法控制它。
@martinstoeckli 虽然您认为 BCrypt 自己应用盐是正确的,但盐 + 哈希的存储取决于您作为开发人员。因此,您可以轻松地在 salt+hash 中添加胡椒粉并将其持久化到数据库中。然后,在随后的检索中,您从数据库中读取值,去除辣椒值,并将剩余的值传递给 BCrypt。
@PeterToTheThird - 这会否定辣椒的优势。辣椒添加了一个服务器端的秘密,并且只有在它保持秘密的情况下才有效(与盐相反)。一个典型的攻击是 SQL 注入,当有人获得了对数据库的访问权而不是对代码的访问权时,那么存储在数据库中的辣椒将毫无用处。大多数 BCrypt 实现会自动将盐添加到生成的散列值中,因此该值已经包含盐、成本因子、算法和散列。此字符串可以存储在 60 个字符长度的单个字段中。
另外,当使用 BCrypt 等“密钥强化”功能时,您无法控制盐的使用。但是,如果您想使用胡椒,您只需将胡椒附加到盐上,并将其用作“胡椒盐”来代替散列函数的“盐”输入。那么,“pepper”是一段合适的数据,它不存储在数据库中,而是嵌入在验证码中,或者存储在另一个安全位置。我从通用的角度处理了这个问题,使用 SHA-512 作为示例函数,但 BCrypt 等也可以以类似的方式使用。
@martinstoeckli - 是的,实际实现取决于您使用的哈希函数。显然,在实现身份验证逻辑时,您需要考虑哈希函数的参数和输出。最终,胡椒只是引入散列函数的另一个变量,它与盐和散列存储在同一位置。
n
nobody

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

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

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


但是,如果所有散列密码都被泄露,包括它们匹配的盐,会发生什么?这不是一样不安全吗?
@mghaoui但是,如果您想知道“密码”,您仍然必须为每种盐构建一个彩虹表,除非某些盐是相同的。
D
DaNeSh

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

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


但是,如果应用程序可以对两个数据库进行身份验证,那么如果攻击者已经破坏了应用程序代码,它是否与一个数据库基本相同?
E
Eric Jin

盐的目的是使所有彩虹表无用,并需要制作一组新的彩虹表。 猜测字符串的时间与制作彩虹表的时间一样长。例如,“密码”的 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

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


Y
Yilmaz

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

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

 userPassword + salt

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

 hash(userPassword+salt)

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

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

 hash(userPassword+salt)=ashdjdaskhfjdkhfjdashadslkhfdsdh

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

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

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

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


e
explogx

如果您使用使用固定大小字符串作为 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"

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