OAuth:如何从javascript隐藏API密钥

时间:2016-07-21 20:11:55

标签: javascript oauth-2.0 aes

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

我一直在阅读关于AES加密和OAuth2的内容,并决定实施以下概念的解决方案:

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

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

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

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

    那么我的选择是什么?

    我是否因为它is doomed而放弃了Javascript?有没有办法存储一个秘密密钥,而不会在将其硬编码到.js脚本中时将其暴露出来?我应该生成一个新的临时密钥,仅用于登录呼叫,并在请求服务器现时将其发送给用户吗?

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

2 个答案:

答案 0 :(得分:2)

了解哪些措施可以防止哪些安全漏洞,这很好。

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. 只发送会话密钥。这已经足够了。

  7. 修订是:

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

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

    3. 对于每个请求:

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

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

答案 1 :(得分:1)

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

这就是OAuth2 Framework协议定义允许公共客户端交互并且不需要任何客户端身份验证的Implicit grant type流的原因。您还可以考虑使用RFC7636来保护访问令牌的发布。