你把盐串放在哪里?

在为数据库存储散列密码时,我总是使用适当的每项salt字符串。对于我的需要,在DB中将盐存储在散列密码旁边总是很好。

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

从安全的角度来看,把盐存放在不同的地方值得吗?考虑一个web应用程序,其服务器代码和DB位于同一台机器上。如果salt存储在该机器上的平面文件中,那么如果数据库受到威胁,那么盐类文件也会受到威胁。

对此有什么建议的解决方案吗?

89374 次浏览

彩虹表的重点是,它们是预先创建的,并大量分布,以节省其他人的计算时间——实时生成彩虹表所需的时间与直接破解密码+salt组合所需的时间一样长(因为在生成彩虹表时,实际上所做的是预先运行计算以进行野蛮强制哈希),因此,通过知道盐就可以“生成彩虹表”的说法是虚假的。

在单独的文件中存储salt没有实际意义,只要它们是基于每个用户的——salt的意义只是为了使一个彩虹表不能破坏数据库中的每个密码。

通常,它们被前置到散列中并存储在相同的字段中。

没有必要单独存储它们——关键是为每个密码使用随机盐,这样单个彩虹表就不能用于整个密码哈希集。对于随机盐,攻击者必须单独强行执行每个哈希(或者为所有可能的盐计算一个彩虹表——工作量大得多)。

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

我将对此提供一个略有不同的看法。

我总是把盐和咸密码散列混合在一起存储。

例如,我将把盐的前一半放在密码的盐散列之前,把盐的后一半放在密码的盐散列之后。应用程序知道这种设计,因此可以获取该数据,并获得salt和salt -password散列。

我采用这种方法的理由是:

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

如果加盐密码哈希是按原样存储的,那么可以执行暴力攻击来获得一个密码,该密码在加盐和哈希时产生与加盐密码哈希相同的数据。

然而,例如,即使咸化密码哈希按原样存储,但预先挂起了一个随机字节,只要攻击者不知道第一个字节将被丢弃,这也会增加攻击的难度。您的应用程序将知道在用于身份验证用户时丢弃数据的第一个字节。

结论是…

1)永远不要以完全相同的形式存储身份验证应用程序使用的数据。

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

更进一步。

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

在生成随机盐时,您还可以随机决定在咸化密码散列之前/之后存储盐的比例。

例如,生成512字节的随机salt。您将salt附加到密码,并获得您的salt密码的SHA-512哈希值。您还生成一个随机整数200。然后存储salt的前200个字节,然后是咸化密码散列,然后是salt的其余部分。

在验证用户的密码输入时,应用程序将传递字符串,并假设数据的前1个字节是salt的前1个字节,然后是咸化散列。这一关会失败的。应用程序将继续使用数据的前2个字节作为盐的前2个字节,并重复此操作,直到在使用前200个字节作为盐的前200个字节后发现阳性结果。如果密码错误,应用程序将继续尝试所有排列,直到找不到为止。

这种方法的优点:

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

这种方法的缺点:

由于精确的逻辑是在运行时推断出来的,因此这种方法是cpu密集型的。盐的长度越长,这种方法的cpu密集度就越高。

验证错误密码将涉及最高的CPU成本。这可能会对合法请求产生反作用,但会增加针对攻击者的安全性。

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

基于ASP的开发。NET MVC 4 Web应用程序的书由William Penberthy:

    要访问存储在单独数据库中的盐,黑客需要侵入两个数据库 不同的数据库来访问盐和盐的密码。存储在 与密码相同的表,甚至是同一数据库的另一个表 意味着当黑客获得访问数据库的权限时,他们将可以访问两个 盐和密码哈希。因为安全包括进行黑客攻击的过程 进入系统太昂贵或耗时,不值得,数量翻倍 黑客必须获得的访问权限应该会使系统更安全 易用性是将盐保存在与数据库相同的数据库中的主要原因 哈希密码。您不必确保两个数据库总是可用的 同时,始终保持同步。有盐的好处是微乎其微的,如果 每个用户都有一个随机的盐,因为尽管它可能会发现一个人的盐 密码更容易,破解密码所需要的力量量 系统整体将是高的。在这个层面的讨论中,这才是真正的期望 是:保护密码。如果黑客已经获得了数据库的副本,您的 应用程序数据已经泄露。在这一点上,问题是减轻用户的 维护两个独立连接的数据库的需求是广泛的。当然,这 增加了安全的感觉,但它唯一的好处是保护 一个密码,一个数据元素。如果数据库中的每个字段都是单独的 加密的,用的是同样的盐,储存起来更有意义 与数据分开,因为您的系统的基本安全性得到了增强

盐的作用是让所有彩虹桌都变得无用,需要重新制作一套彩虹桌。猜一个字符串的时间和做一张彩虹表的时间一样长。 例如“password”的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.

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

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

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

 userPassword + salt

我们把这个传递给哈希算法。

 hash(userPassword+salt)

由于salt是随机生成的,userPassword+salt就变成了一个随机值,绝对不是最常用的密码之一。因此恶意用户通过检查彩虹表就不会知道使用了什么密码。

现在salt值被放在哈希值之前,因为在用户登录时再次使用它来比较传递的凭据与保存的凭据。

 hash(userPassword+salt)=ashdjdaskhfjdkhfjdashadslkhfdsdh

这是密码如何存储在db:ashdjdaskhfjdkhfjdashadslkhfdsdh.salt

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

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

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

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

使用10个字符的salt和固定的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"

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