OAuth:如何从 javascript 中隐藏 API 密钥

Con*_*orU 7 javascript aes oauth-2.0

我们正在迁移我们基于 MVC 的服务器应用程序并制作一个 REST-ful API,通过它可以处理调用。

我一直在阅读 AES 加密和 OAuth2 并决定实施一个从这些概念发展而来的解决方案,如下所示:

  1. 客户端发送登录请求,提供用户 ID 或电子邮件。此请求使用 API 密钥进行 HMAC。
  2. 服务器检查用户 ID/电子邮件是否与现有帐户匹配,如果找到,则创建并存储服务器随机数,将其作为响应的一部分发送给客户端。
  3. 客户端创建他们自己的客户端随机数,并从 API 密钥和两个随机数创建一个新的临时密钥。然后它发送一个登录请求,并使用这个临时密钥加密的密码[为了增加熵并避免以明文形式发送密码]。
  4. 服务器使用它在这个平台上为这个客户端存储的最新随机数解密密码和 HMAC [移动和网络客户端可以有自己不同的随机数和会话] 以及以明文发送的客户端随机数,如果 HMAC检查它然后根据数据库[PBKDF2散列和加盐]验证密码。
  5. 如果请求有效并且密码和用户 ID 匹配记录,则在该平台上为该用户 ID 创建一个新的会话密钥,并将此密钥发送到客户端,并将用于此后来自客户端的每个 API 请求的 HMAC。
  6. 任何新的非登录请求都将包括根据会话密钥和随机化 IV 计算出的 HMAC 签名。

所有通信都通过 TLS 处理,因此这是增加的安全性,而不是唯一的防线。

在移动应用程序上,这会起作用,因为您可以在配置文件中隐藏移动应用程序的密钥,这提供了一些不错的安全措施 - [也许不是很多,我不完全确定]但是如果我们尝试转换所有请求从我们的网页到这种形式,这意味着使用 Javascript 来处理客户端 AES 加密和身份验证,并且……正如本文清楚地解释的那样,“如果您将 API 密钥存储在 JavaScript Web 应用程序中,您不妨打印一下它在主页上以粗体大写字母显示,因为全世界现在都可以通过浏览器的开发工具访问它。”

我可以只使用随机数作为 API 密钥——或者完全放弃对这些请求使用 AES 加密,并尝试通过其他方式(例如 CSRF 令牌)进行验证,并确保所有请求都以某种方式来自我们自己的前端——但是如果我们想创建一个允许与其他页面或服务集成的 API,这将不起作用,即使那样,我将如何保护客户端的秘密会话密钥?

这篇文章建议生成一次性 cookie 作为令牌,但这是一个有限的解决方案,适用于海报的服务,但不适用于我们。我希望能够 HMAC 用户发送的每个请求都带有一个用户特定的密钥,该密钥可以过期并被重置,并且由于该服务最终将处理资金,我希望请求身份验证被严格锁定。

那么我的选择是什么?

我是不是就放弃了 Javascript,因为它注定要失败?有什么方法可以存储密钥而不会像硬编码到 .js 脚本中的那样将它清楚地暴露出来?我是否应该生成一个仅用于登录调用的新临时密钥,并在用户请求服务器随机数时将其发送给用户?

此外,我链接到的帖子首先建议使用 cookie 来存储客户端的 Session 密钥,然后从 JS 访问该密钥。这是可以的,还是会提供比密封更多的孔?

Hal*_*yon 3

最好知道哪些措施可以防止哪些安全漏洞。

JavaScript 不太适合加密,这是正确的,因为没有地方可以存储秘密。也没有好的加密库,因为您不应该在 JavaScript 中进行加密。

会话密钥可以作为认证密钥。如果您使用 TLS,您的连接是安全的,攻击者无法知道会话密钥。此外,JavaScript 不需要知道会话密钥。默认情况下,每次请求都会发送 Cookie。并且您可以将 cookie 设置为仅限 http 的 cookie。您不必这样做,但它确实增加了另一层安全性。

您可以给会话 cookie 一个很长的过期时间,这样它基本上就像一个秘密的 API 密钥一样工作。浏览器将负责安全地存储 cookie。建议经常轮换会话密钥,通常是在每个新会话开始时以及身份验证信息更改(例如密码重置)时。

CSRF 令牌可防止重放攻击。绝对建议使用 CSRF 令牌来保护修改请求。您不需要对每个请求进行 CSRF 检查,只需修改敏感信息的请求(例如您的登录凭据,或者在您的情况下:交易)。对于 CSRF 令牌,您可以使用与会话密钥相同的方法:将其存储在 cookie 中。

关键是 JavaScript 不需要知道这些。

我相信您也意识到一件重要的事情是您生成的任何密钥或随机数都必须是加密安全的。不要使用低熵函数。

所以:

  1. 您无需加密用户 ID 或电子邮件,TLS 已为您完成此操作。此外,您也可以发送密码,无需在第 3 步中单独发送。我们不会在 JavaScript 中进行任何加密。所有加密均由 TLS/HTTPS 单独处理。

  2. 如果您有单独的身份验证服务器(例如单点登录),则此方法很好。否则你可以跳过这一步。

  3. 你不需要这个。

  4. 服务器不需要解密任何内容,加密由 TLS 处理。如何存储密码本身就是一个主题,但我认为您已经明白了。

  5. 好的。同样,客户端不应该加密任何内容。

  6. 仅发送会话密钥。已经足够了

修改后的内容是:

  1. 客户端发送登录凭据。连接必须安全。

  2. 服务器验证凭据并将身份验证令牌作为 cookie 发送,并在会话列表中跟踪身份验证令牌。

对于每个请求:

  • 客户端包含身份验证令牌。如果您使用 cookie,这会自动发生。

  • 服务器验证身份验证令牌,并可能生成客户端从此后将使用的新令牌。