使用 HMAC 传输密码可以吗?

信息安全 加密 密码学 Web应用程序 哈希
2021-08-16 16:20:20

我正在编写一个小型 web 应用程序,我不想将登录密码作为明文传输。由于我没有可用的 SSL,我编写了一个一次性质询系统,该系统使用登录表单发送一个随机字符串,然后用于在客户端使用 HMAC-SHA256 对密码进行哈希处理。我使用随机挑战字符串作为消息,使用用户密码作为 HMAC 的密钥。

我知道自制解决方案通常是一个坏主意,所以我想问一下我是否在这里犯了错误。

编辑:似乎有一个误解,我不是在问密码存储或挑战/响应系统,而是关于 HMAC 和密码传输。澄清一点:我的第一个版本是HMAC(msg=password, key=challenge),因为我想,嗯,我要传输到服务器的消息是用户的密码。但后来我注意到挑战字符串并不完全是一个秘密密钥,所以我换了它,但这让我想到我是否可能做错了什么,我想我最好问一下。

4个回答

除了是一个自行发明的方案之外,我认为您的提案至少存在以下问题:

  • 没有与实际数据的绑定。当您验证用户时,您不想真正验证“用户”;您要对会话进行身份验证,即确保链接上的内容将保证来自该用户,并且您将在链接上发送的内容将仅由预期用户看到。主动的攻击者可以劫持连接,甚至被动的窃听者也可以监视您之后发送的任何内容。

  • 该方案容易受到离线字典攻击。密码很弱,因为它们适合人脑,而且人脑不太擅长记住长密码(而且人类用户缺乏耐心输入长密码)。这是一场失败的战斗:计算机经常获得更多的权力,但人类却没有。尝试潜在的密码往往是成功的攻击,并且观察您的方案会产生足够的信息来尝试密码(攻击者只需计算每个猜测的密码的单个 HMAC,并查看它是否与观察到的相符)。通过采用缓慢的派生过程(例如bcrypt )可以在很大程度上减轻此类攻击,但单个 HMAC 是不够的。甚至允许离线字典攻击仍然是一个坏主意。

  • 服务器将必须存储一些足以让用户进行身份验证的信息。这意味着攻击者获得对服务器存储的临时读取访问权限(例如,通过某些 SQL 注入进行数据库转储)可能会获得密码(或密码等效数据),从而允许他以极低的成本冒充用户。更好的密码方案在服务器上存储足够的数据来验证密码,但仅此而已。

为数据传输制作适当、安全、经过身份验证的介质并非易事。如果您确实需要自己重新实现某些东西而不是使用一些 SSL/TLS 库,那么请帮自己一个忙并为此实现一个标准协议,例如SSL/TLS如果您对证书有疑虑,请考虑使用 SRP 的 TLS(根本没有证书,基于相互的客户端-服务器密码身份验证,不易受到离线字典攻击)。

编辑:关于您在自己的编辑中的澄清:使用秘密数据(密码)作为 HMAC 中的密钥,使用公开数据(挑战)作为 HMAC 中的数据,理论上比相反的要好得多(HMAC 的设计密钥是秘密的);实际上,鉴于 HMAC 的结构,它可能不会对安全性产生太大影响。然而,我上面暴露的观点仍然适用。

您的挑战/响应方案至少有两个大缺点:

  1. 您必须在服务器端存储纯文本密码(或纯文本等效项)。

  2. 您必须在客户端进行密码处理,这意味着无法保证客户端正在执行“有效”的代码片段来处理密码烹饪。例如,如果使用 javascript 来处理“密码烹饪”,您无法确定客户端是否正在执行恶意 javascript,它(例如)将纯文本密码直接发送给攻击者。这可能由 XSS 发生,或者攻击者能够在 HTTP 流量到达客户端之前对其进行修改。

因此,您可以增强您的挑战/响应方案。看看http://openwall.info/wiki/people/solar/algorithms/challenge-response-authentication这些算法解决了“纯文本密码存储问题”。

但即使使用那些“增强的质询/响应身份验证算法”,第二个缺点仍然存在。并且没有可移植/可靠的方式来处理它(“SSL”除外)。浏览器扩展可能有助于减少与基于 javascript 的“密码烹饪”操作(在客户端)相关的一些问题,但它不如安全的 HTTPS 连接好。

这个问题没有具体说明它要防御的威胁模型是什么,但我怀疑它提出的方案在实践中是不够的。我的感觉是,在实践中,在任何明文密码存在问题的威胁模型中,挑战-响应提议也不够充分。

实际上,在攻击者可以窃听您的通信的大多数情况下,攻击者还可以篡改您的通信并发起中间人攻击。最突出的例子是通过开放无线网络进行通信,但其他包括 DNS 欺骗、客户端恶意软件、恶意 HTTP 代理等。如果攻击者可以发起中间人攻击,那么挑战-response 提议不安全:攻击者可以注入恶意 Javascript 来窃取密码或做其他令人讨厌的事情。

因此,我认为挑战-响应提议提供了一种虚假的安全感,因为它不对服务器提供的内容和代码进行身份验证。我怀疑,您应该使用 SSL/TLS(即 HTTPS),而不是部署您的解决方案:如果您需要明文传输的密码有问题,那么您可能需要 SSL/TLS。

要对客户端进行身份验证,您的方法听起来几乎是正确的。但是,它确实要求您在服务器上存储明文密码。对此的一个小调整是存储从密码而不是密码本身派生的内容。然后在客户端,您导出相同的信息以输入 HMAC。

不过要非常小心,这不是相互身份验证!客户不会对您的网站进行身份验证,并且可能会被欺骗/欺骗以提供他/她的密码。

你有没有想过重放攻击?

离线暴力攻击怎么样?

我正在做的是强调你最后一句话的第一部分。