【问题标题】:OAuth: How to hide API Secret Key from javascriptOAuth:如何从 javascript 中隐藏 API 密钥
【发布时间】:2016-11-25 14:14:08
【问题描述】:

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

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

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

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

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

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

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

那么我有什么选择?

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

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

【问题讨论】:

    标签: javascript oauth-2.0 aes


    【解决方案1】:

    很高兴知道哪些措施可以防止哪些安全漏洞。

    你说得对,JavaScript 不太适合加密,因为没有地方可以存储秘密。也没有好的加密库,因为您不应该在 JavaScript 中进行加密。

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

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

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

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

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

    所以:

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

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

    3. 你不需要这个。

    4. 服务器不需要解密任何东西,加密由 TLS 处理。如何存储密码是一个独立的话题,但我认为你已经知道了。

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

    6. 只发送会话密钥。够了。

    修改为:

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

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

    对于每个请求:

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

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

    【讨论】:

    • 所以你是说如果客户端获得会话密钥作为 cookie 并发送它就不需要 HMAC 签名?我将如何使用您修改后的身份验证模式来防止重放攻击?
    • CSRF 令牌可防止重放攻击。它是由服务器创建的一次性令牌: 1. 客户端请求/change_password 2. 服务器创建 CSRF-token 并将其存储在令牌列表中(具有合理的过期时间),它将 CSRF-token 包含在/change_password 的内容(例如action="/do_password_change?csrf=[token]" 的形式。3. 客户端发送带有请求的 CSRF 令牌。4. 服务器检查 CSRF 令牌并将其从令牌列表中删除。这可以防止重放攻击,因为第二次使用 CSRF令牌将产生一个无效请求(服务器知道它已被使用)。
    • 我要读一些关于 CSRF 的 mroe。我想在我将您的答案标记为已接受之前等待,以防有人提供更多输入或替代想法。谢谢!
    【解决方案2】:

    移动应用应被视为public clients。这意味着他们不应该存储任何秘密。无论您将使用哪种加密算法,都不会阻止客户端凭据被泄露。

    这就是为什么 OAuth2 框架协议定义了Implicit grant type 流,它允许公共客户端交互并且不需要任何客户端身份验证。您也可以考虑使用RFC7636 来保护访问令牌的颁发。

    【讨论】:

      猜你喜欢
      • 2021-12-10
      • 2021-08-05
      • 2016-11-09
      • 1970-01-01
      • 2021-01-27
      • 2020-04-15
      • 2020-05-12
      • 2022-01-02
      • 1970-01-01
      相关资源
      最近更新 更多