34

在使用S3 样式身份验证的 RESTful API 中,API 客户端使用 HMAC-SHA1 使用其密钥对请求进行签名,因此密钥永远不会通过线路传输。然后,服务器通过使用客户端的密钥来验证客户端,以重复签名过程本身并将结果与​​客户端传输的签名进行比较。

这一切都很好,但这意味着服务器需要访问客户端共享密钥的明文。这违背了所有反对在数据库中明文存储用户密码的建议。据我所知,仅存储密码的加盐哈希不是一种选择-因为那样我就无法验证客户端的签名。

我应该强调我的 API 是 RESTful 的,因此应该是无状态的:我宁愿在其他 API 调用之前避免登录步骤。

一种可选的解决方案是使用某种对称密钥算法加密所有用户密码。但是,服务器必须将加密的密钥存储在易于访问的地方,例如源代码内部。这总比没有好,但不是最佳解决方案(正如@Rook 在他的回答中提到的,它违反了 CWE-257)。

解决方案的另一个方向可能是围绕非对称签名,但我不知道如何将其应用于 HMAC,也找不到任何关于该主题的文章。

我在这里遗漏了一些明显的东西吗?许多受人尊敬的提供商已经实施了这种身份验证方案——他们不可能都违反共同的安全原则,不是吗?如果没有,您是否可以分享任何最佳实践?

4

3 回答 3

39

这是对称密钥质询-响应式身份验证的缺点——您不会将秘密放在网络上,但您必须在两端存储秘密。(HMAC 是对称密钥系统)。

请注意,它不是密码- 它是共享机密。这里有一个根本的区别——密码通常由用户选择,而共享密钥是随机生成并提供给用户的(在这种情况下,它们通常被称为“API 密钥”)。

以可逆格式存储密码是不好的,因为如果您的数据库遭到破坏,那么攻击者就会获得可能(并且可能已经)在其他地方使用过的密码。另一方面,存储共享密钥并不是一个问题——它是特定于您的服务的密钥,因此攻击者所获得的只是登录您的服务的能力。

另一方面,可以一个非对称系统,不必在服务器端存储秘密。基本思想是服务器知道客户端的公钥和当前的消息序列号。发送 API 请求时,客户端会增加消息序列号,并在序列号和 API 请求参数上计算签名,服务器可以使用公钥对其进行验证。如果消息包含旧消息序列号,服务器将拒绝该消息,以防止重放攻击。

于 2011-03-31T07:08:30.243 回答
11

理想情况下,在用户登录后,您可以给他们一个 Cryptographic Nonce,该 Nonce 在该会话的整个生命周期内用作 HMAC 密钥 K。这是一种安全的方法,但它不是 RESTful,因为 REST 是无状态的。这种每次登录都会发布消息验证码的想法在技术上是一种状态形式。

加密密码并将其存储在数据库中是违反CWE-257 的

于 2011-03-30T16:43:46.140 回答
2

我不确定我是否在这里遗漏了什么,但一种选择是使用散列密码作为对称密钥。

于 2013-04-05T09:01:16.107 回答