双重哈希密码 - 客户端和服务器

9

嘿,首先,我不是在问像md5(md5(...这样的问题,已经有相关主题了。

我的问题是:

我们允许客户在本地存储他们的密码。自然地,我们不希望它们以明文形式存储,因此我们在本地进行hmac处理,然后再进行存储和/或发送。现在,这很好,但如果这是我们所做的全部内容,那么服务器将拥有存储的hmac,而且由于客户端只需要发送hmac,而不是明文密码,攻击者可以使用从服务器获取的存储哈希来访问任何人的帐户(在某些情况下,例如某些人获得了对数据库的访问权限时,这将是灾难性的)。

因此,我们的想法是在客户端通过hmac对密码进行一次编码,将其发送到服务器,然后在服务器上通过hmac再次对其进行编码,并将其与存储的两次hmac处理后的密码进行匹配。这将确保:

  • 客户端可以在本地存储密码,而无需将其存储为明文
  • 客户端可以发送密码,而无需过多担心其他网络方面的问题
  • 服务器可以存储密码,而无需担心有人从服务器窃取密码并用其登录。

自然地,所有其他事项(强密码、双重盐等)也适用,但与问题无关。

实际的问题是:这听起来像是一个可靠的安全设计吗?我们有没有忽略任何缺陷?也许有一个类似于此的安全模式吗?

补充说明:我们不希望在客户端以明文形式本地存储密码,因为尽管很遗憾,但仍有很多人将相同的密码用于多个服务,因此获取“真实”密码对用户而言将是更大的安全漏洞。


md5()是不安全的。此外,这也不是一个安全协议。 - rook
7个回答

12

我要赶快出门了,但是 Skeet 发来了消息,你不能忽略它。

你所做的是用另一个固定值替换密码。这里你没有获得任何安全性,唯一的保护就是明文密码不会在客户端机器上被发现。

接下来你似乎是将密码的 HMAC(确定你指的是 HMAC 吗?如果是,密钥从哪里来,存储在哪里?)视为密码本身——你将其从客户端发送到服务器进行身份验证。第二个 HMAC 或哈希运算毫无意义——你只是将其与发送的值进行比较——它就像是另一种形式的密码。因此,作为攻击者,我只需要窃取存储在客户端机器上的 HMAC,而不是窃取密码。这里完全没有任何收益。


9
我更愿意让某个随机攻击者知道我的哈希值,而不是我实际使用并可能在不同地方重复使用的密码。 - Joe Phillips
1
我不理解这个答案。客户端存储的密码不是明文,这很好。而且,即使攻击者能够访问服务器上的双重哈希密码表,也不能登录,这也很好。当然,访问客户端存储的哈希仍然允许登录,而访问服务器表意味着大麻烦,但我仍然相信提出的想法可以改善整体安全性。 - mafu
如何实现呢?你所做的只是将明文密码替换为其哈希版本。哈希版本现在成为了密码,你只是用二进制密码替换了文本密码。这个二进制密码仍然以明文形式存储,并且可以被发现和使用。唯一的好处是隐藏原始密码,但这只有在用户在其他地方使用相同密码时才有益。 - blowdart
1
密码重用很普遍,我们无法阻止用户这样做。鉴于这些事实,该方案降低了破坏其他系统的风险,这确实具有价值。从另一个角度来看:如果每个其他网站都实施了一种方案,使其服务器永远不会在内存中保存用户的明文密码,那么它将增加您自己系统的安全性。如果你能挥动魔杖让每个其他网站都这样做,你会吗? - Alex von Brandenfels

7
警告:我不是安全专家。我会联系blowdart看看他是否有兴趣加入。
如果客户端只是存储哈希,并且有效地基于哈希传输某些内容,那么它们实际上是以明文形式存储它。第一个哈希提供的唯一好处是,如果他们在不同的系统上使用了相同的密码,那么其他系统在哈希被揭示后不会受到损害。
换句话说:如果有人可以获取存储在服务器上的哈希值,那么这就是他们登录系统所需的全部内容...就像明文存储一样。

好观点。所有的哈希和双重哈希让我有一瞬间感到困惑,以为它非常安全! :) - Sev
我不确定那该怎么做。假设你得到了存储在服务器上的哈希值 - 你会如何使用它来登录? - J. Stoever
@J. Stoever:嗯,我更多的意思是客户端存储的哈希值...但如果它们是相同的东西,那就无所谓了。基本上,在那一点上,您已经拥有客户端用于登录的所有内容...您没有原始明文密码并不重要。 - Jon Skeet

6

正如其他人所说,就客户端和您的系统而言,这并没有给您带来任何好处——第一个哈希只是成为了密码。

如果客户端在其他系统上使用相同的密码(这很可能),那么这个方法就有用了。在这种情况下,如果客户机被攻破,那么至少你本地的哈希密码副本不会允许攻击者访问其他系统。显然,攻击者现在可以访问您的服务器——毕竟他们已经得到了密码。

攻击者能够访问服务器上的双重哈希值对他们来说毫无意义,因为他们无法将其反转以获取单个哈希(即“密码”)。当然,如果攻击者能够读取您的安全数据库,那么我认为他们还有其他攻击向量可用 :)

此外,正如另一位发帖者所说,请确保在两个哈希上都使用盐。如果密码不够强大,不这样做的话,反转哈希实际上可能相当简单。

编辑-实际上,仔细想想,由于您正在使用哈希作为密码,因此您实际上不需要在服务器上使用盐。没人能够创建有效的彩虹表:) 但在客户端仍需要使用盐。


这基本上描述了我所想的,除了服务器上不需要盐巴之外。但是我想,即使如此使用盐巴也没有什么坏处。当然,如果客户端存储密码,就无法确保其不会受到损害,但我并不打算解决这个问题。 - J. Stoever

3
不,这并不安全。哈希值实际上是密码。事实上,密码是从用户认为的“主”密码派生出来的并不重要。它是一种用于验证用户的秘密值,对吧?听起来像是密码。
我认为关键在于这句话:“当然,我们不想以明文形式存储它们,因此我们在本地进行hmac处理,然后再存储和/或发送。”如果系统被更改,以便哈希密码现在具有与密码曾经相同的权限,则应该对哈希密码使用相同级别的谨慎。

虽然大家都反对这个协议,但是这篇文章写得很好,我认为楼主会理解的。+1 - rook

1

blowdart说得一点也没错 - 你只是在改变窃取密码的方法,而不是保护任何东西。你试图复制的是一种我现在想不起名字的旧身份验证协议。它的工作原理如下:

初始化时,服务器获取您的密码,迭代哈希n次,表示为Fn(pass)。客户端拥有密码和数字n。

您进行身份验证,并发送Fn-1(pass)给服务器 - 即将密码哈希n-1次。服务器再哈希一次,将其与Fn(pass)进行比较,如果匹配,则获得访问权限。服务器然后用Fn-1(pass)替换Fn(pass),并将n减少。

下次进行身份验证时,您发送Fn-2(pass),过程重复。

让我们来检查一下安全性:

  • MITM: 协议中没有内置防御措施,您需要在SSL内部添加层级。
  • 重放攻击:它们不起作用,因为服务器已经减少了哈希迭代次数。
  • 窃听:下一个身份验证将使用Fn-1(pass)进行。您拥有的是Fn(pass)。根据哈希函数的定义,从Fn(pass)到Fn-1(pass)是不可行的。
  • 拥有服务器:您不知道客户端的密码,也无法像他们一样进行身份验证,因为再次需要Fn-1(pass),而您只有Fn
  • 拥有客户端:由于您存储了Fn-1(pass)(以便他们不必输入密码)-拥有客户端将让攻击者登录。如果仅存储n而不是密码,则可以防止此类攻击,但显然您想保存密码。

这就是你想要实现的目标。然而,这个协议没有被使用的原因是它非常难以同步。如果客户端和服务器由于一个未完成的步骤而失去同步,你将被锁定在外面。你为了避免这个问题而建立的任何弹性可能会减少重放或窃听的安全性。


1
这是一种一次性密码方案,例如RSA SecurID和S/KEY。参见http://www.ietf.org/rfc/rfc2289.txt和http://en.wikipedia.org/wiki/One-time_password。 - President James K. Polk

0

我不确定相比于明文本地存储密码,这样做能为您带来什么好处。

本地加密的目的是防止黑客能够将密码发送到您的服务器。但是,如果您要发送加密形式...那么,您什么也没得到。

相反,本地计算机应该以双向加密格式存储密码。这意味着它可以被解密。在传输之前进行解密。数据库可以使用单向加密格式进行存储(甚至使用单独的加密机制)。在比较之前,您需要对接收到的内容进行加密,然后再进行检查。


消息摘要函数不是加密的方法。 - rook

0

密码的初始哈希有什么作用?它将防止发现密码的明文版本。但它不能防止使用该哈希值计算双重哈希值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接