为啥 OWASP 不建议在客户端和服务器上都对密码进行 bcrypt?
Posted
技术标签:
【中文标题】为啥 OWASP 不建议在客户端和服务器上都对密码进行 bcrypt?【英文标题】:Why doesn't OWASP recommend to bcrypt the password both on the client and the server?为什么 OWASP 不建议在客户端和服务器上都对密码进行 bcrypt? 【发布时间】:2018-11-15 01:11:06 【问题描述】:由于最近 GitHub 和 Twitter 出现问题:
GitHub Accidentally Recorded Some Plaintext Passwords in Its Internal Logs Twitter Admits Recording Plaintext Passwords in Internal Logs, Just Like GitHub我想知道,为什么最好不要在客户端和服务器上同时加密密码?由于我不会更改已经是服务器端最佳实践的任何东西(盐、强哈希、HTTPS),它只会更安全。服务器会将已经散列的密码视为密码,并在存储之前再次对其进行散列。
如果我在抛出异常时记录整个请求,如果登录/注册请求中发生异常,我将永远无法访问用户明文密码 我知道,如果有人可以通过 MITM(许多公司在其私有网络中替换 SSL 证书)或通过日志或恶意服务器管理员访问这些仅客户端散列的密码,他们将能够使用它在我的网站中进行身份验证,但无法访问明文密码,因此它永远不会危及用户在其他网站和服务中的帐户(即使对于那些重复使用密码的用户)【问题讨论】:
【参考方案1】:我正在寻求解决可以在服务器上登录纯文本密码的类似问题。结论是如果可能,您应该始终另外散列客户端密码。
这里有一些关于client-plus-server散列的文章:
Client-Plus-Server Password Hashing as a Potential Way to Improve Security Against Brute Force Attacks without Overloading the Server
Salted Password Hashing - Doing it Right
具体见:
在 Web 应用程序中,始终在服务器上散列
如果您正在编写一个 Web 应用程序,您可能想知道在哪里散列。 如果密码在用户浏览器中使用 javascript 进行哈希处理, 还是应该“以明确的方式”将其发送到服务器并在那里进行散列?
即使您在 JavaScript 中对用户密码进行哈希处理,您仍然可以 必须散列服务器上的哈希值。考虑一个散列的网站 用户浏览器中的用户密码,无需散列哈希值 服务器。为了对用户进行身份验证,该网站将接受哈希 从浏览器中检查该哈希是否与 数据库。这似乎比在服务器上散列更安全, 因为用户的密码永远不会发送到服务器,但事实并非如此。
问题是客户端哈希逻辑上变成了用户的 密码。用户进行身份验证所需要做的就是告诉服务器 他们密码的哈希值。如果坏人得到了用户的哈希值,他们可以 使用它向服务器进行身份验证,而不知道用户的 密码!所以,如果坏人以某种方式窃取了哈希数据库 从这个假设的网站,他们可以立即访问 每个人的帐户,而无需猜测任何密码。
这并不是说你不应该在浏览器中散列,但是如果你 这样做,您也必须在服务器上进行哈希处理。散列在 浏览器当然是个好主意,但请考虑以下几点 您的实施:
客户端密码散列不能替代 HTTPS (SSL/TLS)。如果浏览器和服务器之间的连接是 不安全,中间人可以按原样修改 JavaScript 代码 下载以删除散列功能并获取用户的 密码。
有些网络浏览器不支持 JavaScript,有些用户在他们的浏览器中禁用了 JavaScript。因此,为了获得最大的兼容性,您的应用 应该检测浏览器是否支持 JavaScript 和 如果没有,则在服务器上模拟客户端哈希。
您还需要对客户端哈希值加盐。显而易见的解决方案是让客户端脚本向服务器询问用户的盐。 不要那样做,因为它会让坏人检查用户名是否是 在不知道密码的情况下有效。因为你在散列和加盐 (用好盐)也在服务器上,使用用户名(或 电子邮件)与特定于站点的字符串(例如域名)连接为 客户端盐。
经过研究,散列客户端似乎也有明显的安全优势。如果通过 HTTPS 的密码被泄露或密码被登录到服务器上,那么纯文本密码就不能轻易地在用户的其他帐户上重复使用(许多用户重复使用他们的密码)。
唯一可能的缺点是客户端性能和服务器端密码验证。用户可以操纵您的客户端 JS 并提交“弱”密码。服务器不会知道更好。但我认为这是一个小问题,它依赖于人们故意修改他们的客户端代码以削弱他们自己的安全性。
【讨论】:
【参考方案2】:可以进行客户端哈希,但我们应该考虑我们真正实现的目标。
您可能想要实现的是,当密码通过(希望是加密的 SSL)连接发送时,攻击者无法读取密码。如果攻击者可以拦截流量,他很可能也可以更改它,因此可以剥离任何执行客户端哈希的 JavaScript。然后整个保护来自服务器端哈希。
您可以实现的是,您可以减少服务器负载,因为您让客户端进行繁重的计算。如果您可以保证客户端的完整性,那么您可以在客户端上进行密钥拉伸并在服务器上使用快速散列。对于已安装的应用程序,这可能是一个选项,但不建议用于网站,因为无法保证客户端的完整性,并且由于 JavaScript 通常较慢,因此您可以减少轮次。
如果攻击者只能监听流量而不能改变它,你会得到一点好处。您愿意花在散列上的时间必须分成客户端部分和服务器部分(不能让用户永远等待)。服务器时间必须足够长才能保证安全,这样客户端就没有多少时间了。如果您在客户端上使用过快的哈希,那么截获的密码哈希仍然在暴力破解的范围内(尽管它是攻击者必须克服的障碍)。
所以简而言之,这通常是不值得的麻烦,优势太小,时间最好投入到服务器上的 hash-time 上。
【讨论】:
这里的主要目标是避免上个月发生的像 GitHub 和 Twitter 这样的案例。或者在公司 MITM 代理的情况下,IT 管理员可以轻松读取请求,但要更改 HTTP 响应的内容以进行攻击,他们必须留下痕迹。我只是想表明,在客户端和服务器中执行此操作比仅在服务器中执行此操作更安全。 @Imcarreiro - 然后去做,即使在客户端上花费几毫秒的时间进行散列,也很难成功地暴力破解传输的密码散列。只要确保服务器上有足够的哈希时间,以防客户端部分被剥离。【参考方案3】:任何哈希(包括bcrypt
)都需要秘密盐 - 阅读here了解更多详情。如果该盐丢失,客户端将无法创建相同的散列 - 这与丢失密码相同。因此,您需要创建一种机制,让您的所有客户都能安全地获取盐。而且您需要确保黑客无法获得这种盐。这实现起来相当复杂。
另一件需要考虑的事情是最终用户设备的限制 - 例如,android 设备的 CPU 非常薄弱,并且远不如普通服务器强大。由于bcrypt
的主要优势是计算哈希所花费的时间,因此您需要选择参数,以便一个好的服务器(甚至可能使用 GPU)能够在较慢的时间内计算它(假设密码 > 1s 20个字符)。这使得创建这些彩虹表变得如此困难。
所以,除非你能保证你的所有用户都在足够强大的设备上运行,否则不建议在客户端做bcrypt
。
【讨论】:
考虑到我已经在服务器端以“正确的方式”做事,在你发布的这个链接中,有这部分:“但即使盐不是秘密,它仍然使使用那些老式彩虹表变得更加困难”,因此,对于客户使用 bcrypt,盐不需要是秘密,我可以使用散列(用户名 + someStaticString)作为盐。我的问题是:为什么不呢? 这让事情变得更加困难——因为黑客必须创建表,这需要大量时间(取决于bcrypt
参数),但它仍然是可能的。因此,如果有足够的激励(例如银行密码),黑客可能会尝试这样做。
另一个需要考虑的问题是设备限制 - 例如,在 Android 设备上,cpu 非常弱,很难在合理的时间内使用足够强大的bcrypt
。考虑到黑客可以使用非常强大的 CPU(甚至 GPU),所以你需要在这样的机器上足够慢的东西。除非你能保证你的所有客户都有足够好的硬件,否则这是一个问题。这也是创建彩虹表变得困难的原因——因此削弱 bcrypt 将使创建表变得更容易。【参考方案4】:
这个方案的问题是它需要服务器信任客户端。特别是,它假设客户端总是会对用户输入的内容进行哈希处理。如果我们打破这个假设,就像入侵者一样,问题就会开始出现。
Bob 有一个来自您的服务器日志的(单哈希)密码列表。这些不是明文密码,但它们不是密码文件中的双哈希密码。但是假设他对您的客户端做了一个小改动:他取出了 bcrypt() 行,因此它不再在发送之前对他粘贴到密码字段中的任何内容进行哈希处理:相反,它只是发送原始文本。
然后他开始发送登录信息。现在您的服务器会看到用户名和单哈希密码(因为这是 Bob 输入的,因为 Bob 知道这些)。它假设这是通常的客户端,所以它会再次对密码进行哈希处理,并检查其文件中的双重哈希密码......并且它已经被哈希两次,所以它匹配。 Bob 不知道明文密码,但通过修改客户端,他做到了,因此他需要知道它。
【讨论】:
但是,据我了解,您描述的所有这些问题如果没有在客户端进行散列,而仅在服务器中进行散列,我就会遇到。泄漏总是会发生并且总是会发生,我建议的是在它之上增加一个额外的安全层,它不会解决所有问题,在这些情况下(Twitter 和 Github)它会暴露用户可能重复使用的明文密码在其他网站和服务中(我们知道他们中的大多数确实会重复使用他们的密码),唯一暴露的就是 Github 和 Twitter 帐户。 这个和纯文本客户端密码没有区别。除了在这种情况下,鲍勃不能窃取您在其他服务上的其他帐户。是的,如果 Bob 知道用户的密码,他就可以登录,但这取决于 2-Factor 和可以减慢暴力猜测的实现。以上是关于为啥 OWASP 不建议在客户端和服务器上都对密码进行 bcrypt?的主要内容,如果未能解决你的问题,请参考以下文章