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

OAuth: How to hide API Secret Key from javascript

本文关键字:隐藏 API 密钥 javascript OAuth      更新时间:2023-09-26

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

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

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

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

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

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

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

那么我有什么选择呢?

既然Javascript注定要失败,我就放弃它吗?是否有某种方法可以存储密钥,而不将其公开为硬编码到.js脚本中的日期?我是否应该生成一个新的临时密钥,仅用于登录调用,并在用户请求服务器nonce时将其发送给用户?

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

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

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,则会自动发生这种情况。

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

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

这就是为什么OAuth2框架协议定义了隐式授权类型流,该流允许公共客户端交互,并且不需要任何客户端身份验证。您还可以考虑使用RFC7636来保护访问令牌的发布。