如何使用 BCrypt 对密码进行客户端哈希处理?

信息安全 javascript 客户端 bcrypt
2021-08-26 17:06:27

我正在将使用 MD5 散列的旧应用程序迁移到带有 BCrypt 密码编码的 Spring Security。我想在新用户创建页面、更改密码页面和登录页面上对密码进行编码,然后再将其发送到网络。

我知道 HTTPS 可以解决这个问题,但我仍然被指示在按照我们的组织指南通过网络发送密码之前对其进行编码。

使用 JavaScript 对密码进行哈希处理的最佳解决方案是什么?

为了进一步解释,我使用 JCryption API 使用 AES 加密密码,所以AES(SHA1(MD5(plain password)))现在我只想替换MD5通过网络传输的值Bcrypt其余的东西保持不变。这种方法对“中间人攻击”有效吗?

4个回答

我知道 HTTPS 可以解决这个问题,但我仍然被指示在按照我们的组织指南通过网络发送密码之前对其进行编码。

确实定义了您的情况。

基本上,您有一个无论如何都应该使用的简单解决方案(使用 HTTPS),如果只是因为没有 HTTPS,一个活跃的攻击者可能会在身份验证步骤之后劫持连接,而不管您对密码使用的任何“编码”和散列(攻击者无论你在仪式上如何使用哈希函数和加密,谁有能力进行中间人攻击都会成功)。然后,您还有一个既愚蠢又在管理上不可避免的指南。那么您的问题是:您如何才能正确地做事,并且仍然遵守“准则”?

请注意,该指南在多个级别上几乎没有意义。不仅缺少 SSL 使协议容易被劫持;而且任何类型的“编码”对被动攻击者也无济于事。原因是,如果协议是“客户端显示‘编码’密码”,那么被动窃听者只会查看那个“编码密码”并自己发送——因此,这种编码实际上并不能提高任何安全性大大地。它只是让一些人感到更安全,因为他们将密码学理解为某种烧烤酱(“我们撒得越多越好”)。

我建议您执行以下操作:

  1. 首先,尝试推销“HTTPS”体现“编码”的想法。换句话说,通过在 SSL 隧道内发送密码,您已经遵守了该指南,因为密码与其余数据一起经过适当编码(并且实际上是加密的)。

  2. 如果愚蠢感染更严重,并且某些审计员(我们称他为 Bob)仍然对您缺乏“编码”感到不满,那么请尝试在客户端应用一些可逆编码。例如,在发送密码之前,应用Base64这意味着如果密码是“bosucks”,发送的内容(当然是在 SSL 内)将是“Ym9ic3Vja3M=”,Bob 会更开心,因为后面的字符串显然更安全。

这里重要的一点是,由于需求没有意义,因此解决方案也没有多大意义。

没有身份验证你就没有安全感

为了进一步解释,我使用 JCryption API 使用 AES 加密密码,所以通过网络传输的值是 AES(SHA1(MD5(plain password))) 现在我只想用 Bcrypt 替换 MD5。其余的东西保持不变。这种方法甚至适用于“中间人攻击”

不,不是的。我无法足够强烈地表达这一点。 现在我可能无法说服您使用 https(您甚至可能别无选择),但我希望我能说服您它绝对不安全,并且您认为它提供一些安全性的任何信念都是不正确的。如果被迫在不使用 TLS/SSL 的情况下继续前进,您至少可以让所有相关人员非常清楚该系统是易受攻击的,并且应该被描述为“容易绕过感觉良好的安全性”。在您当地的星巴克运行 MITM 热点的脚本小子可能会欺骗您的用户并窃取凭据或劫持会话,因此它不需要复杂的技能或复杂的攻击。

如果通信渠道不安全,您不应该期望您发送的代码是客户端实际运行的代码。它被称为 https(安全 http)是有原因的,而不仅仅是 httpe(加密的 http),安全性不仅仅是加密。没有身份验证的加密通信是没有意义的。

需要考虑的一些事情:

  • 如果没有 https,您的用户怎么知道他们在您的登录页面上?
  • 你怎么知道攻击者没有修改发送给用户的登录页面,以便在加密并将其发送给你之​​前向他发送凭据的明文副本?
  • 您正在尝试加密密码的双重哈希,您将如何安全地传输加密密钥?

如果除了加密之外我还进行自己的自定义身份验证怎么办

这不是那么简单。在不安全的通道上引导安全通道并非易事。从理论上讲,您可以构建一个自定义协议来复制 https 的所有方面,包括协议协商、服务器身份验证、公钥基础设施、密钥交换、加密、消息完整性和撤销(我相信我忘记了更多)。一旦你完成了,它就会像 TLS 一样大而复杂。我们还假设它的缺陷比任何 TLS 实现都少,并且您可以在不到几年的时间内完成,您仍然会遇到几乎不可能的问题。您将如何将此客户端组件提供给客户端?“哦,确定让客户端安全地下载它......呃等等”。如果通过不安全的渠道引导安全通信似乎很容易,那么老实说你没有'

现在让我明确一点,TLS 有很多问题,CA 系统也有很多问题。我当然不会假装它是完美的,但安全通信是一个鸡和蛋的问题,它是我们拥有的最佳解决方案。

客户端哈希可以通过 https 完成

从您的措辞看来,您认为这个问题是“客户端散列或服务器端通过 https 散列”,但我希望您能看到这是一个谬误。无论您将如何验证您的用户,都需要 https。 因此,如果您接受您需要 https,那么问题就变成了“为什么或何时使用客户端哈希(通过 https)而不是服务器端哈希(通过 https)有意义?在大多数情况下它不会。 客户端散列更复杂并且不再安全,但它确实有一个优点,那就是服务器对密码的了解为零,而服务器端散列则不是这种情况。在某些情况下,您希望您(服务器)是可否认的。您不能丢失您没有的密钥,并且如果客户从未给您解密密钥,您也不能被迫解密客户的文件。因此,在某些情况下,它可能是强制性要求,但它应该是项目设计的一部分,而不是任意决定。如果您对如何处理零知识场景有具体问题,最好将其作为一个新问题来完成。

bcrypt 不适用于这种类型的客户端散列

bcrypt 的一个关键特性是,当使用相同的明文运行两次独立的时间时,大多数实现会产生不同的哈希值。这是由于使用了盐,它的设计目的是难以查看两个不同的用户是否具有相同的密码。

相比之下,登录表单每次都需要接收相同的数据。毕竟,您将如何验证用户今天输入的密码与他们昨天输入的密码相同?

因此,您有一个设计用于每次生成不同数据的算法,并且您将其输入到需要验证数据每次都相同的应用程序中。这可能表明该算法没有按预期使用。

理想情况下,您应该做的是使用 bcrypt 对服务器上的密码进行哈希处理这就是 bcrypt 的设计目的:在明文不在内存中时保护用户密码,这是迄今为止最常见的状态(例如,如果实施正确,DB 转储只会显示散列密码,而不是明文)。

我个人也看到了客户端散列的一些价值,因为它确实有助于防止可以嗅探流量(或具有服务器访问权限)的攻击者。但是,我不知道有一种方法可以使客户端散列与服务器端 bcrypt 一样安全——这就是为什么您仍然应该使用服务器端 bcrypt。

如果您必须使用 bcrypt 客户端,请使用静态盐

如果您打算使用 bcrypt 对登录表单进行客户端哈希处理,并且希望它成为 MD5 的替代品,则需要使用静态盐。特别是如果您通过 SHA1 传递它,因为这会破坏 bcrypt 盐以及散列数据本身。

当然,这确实打破了 bcrypt 的几个设计假设(例如始终使用随机盐)。

我个人建议使用用户名作为盐(这样很难判断两个不同的用户是否具有相同的密码);但是,我不知道在这种情况下对盐渍进行的任何研究。

AES(或任何对称密码)在这里也没用

请记住,AES 是一种对称算法,这意味着您需要相同的密钥来解密加密数据。

这对你意味着什么?好吧,您可能已经在 J​​avaScript 源代码中获得了您的加密密钥,该源代码将提供给访问您的登录页面的任何客户端。AES 是一种对称密码,因此您的加密密钥(与解密密钥相同)应保密。换句话说,你的私钥是众所周知的——它在这一点上实际上没有提供任何安全性。

您应该改用公钥加密技术,例如 PGP 或 HTTPS(从技术上讲,HTTPS 使用混合方法)。说真的,只需使用 HTTPS,除非您的组织出于某种原因拒绝让您拥有 SSL 证书。请记住,PGP 并不能真正防止主动 MITM 攻击,但它至少可以防止窃听。

最好的解决办法是:不要。

如果您通过 HTTPS 发送密码,散列密码不会提供额外的安全性。

如果您通过纯 HTTP 发送密码,攻击者可以获取散列密码并使用它自己登录;或者,他们可以篡改 JavaScript,在散列之前将密码发送给他们。在任何一种情况下,散列密码都不能提供安全性。