OAuth:如何从 javascript 中隐藏 API 密钥
OAuth: How to hide API Secret Key from javascript
我们正在迁移基于 MVC 的服务器应用程序并制作一个 REST-ful API 来处理调用。
我一直在阅读 AES 加密和 OAuth2,并决定实施一个从这些概念中成长起来的解决方案,如下所示:
- 客户端发送请求以提供用户 ID 或电子邮件登录。此请求是使用 API 密钥的 HMAC。
- 服务器检查 UserID/Email 是否与现有帐户匹配,如果找到,则创建并存储一个服务器随机数,将其作为响应的一部分发送给客户端。
- 客户端创建自己的客户端随机数,并根据 API 密钥和两个随机数创建一个新的临时密钥。然后它发送一个登录请求,其中包含使用此临时密钥加密的密码 [为了增加熵并避免以明文形式发送密码]。
- 服务器使用为该客户端存储在该平台上的最新随机数 [移动和 Web 客户端可以有自己不同的随机数和会话] 以及在清楚,如果 HMAC 检出它然后根据数据库验证密码 [PBKDF2 散列和加盐]。
- 如果请求有效且密码和用户 ID 匹配记录,则在该平台上为该用户 ID 创建一个新的会话密钥,并将此密钥发送到客户端,并将每隔 [=47] 用于 HMAC =] 从今以后请求来自帽子客户端。
- 任何新的非登录请求都将包含从会话密钥和随机 IV 计算得出的 HMAC 签名。
所有通信都通过 TLS 处理,因此这是增加的安全性,而不是唯一的防线。
在移动应用程序上,这会起作用,因为您可以在配置文件中隐藏移动应用程序的密钥,这提供了一些不错的安全措施 - [也许不是很多,我不完全确定]但如果我们尝试将我们网页上的所有请求转换为这种形式,这意味着使用 Javascript 来处理客户端 AES 加密和身份验证以及... well as this article clearly explains,“如果您存储 API在 JavaScript 网络应用程序中键入您还不如在主页上用粗体字打印出来,因为现在全世界都可以通过浏览器的开发工具访问它。"
我只能使用随机数作为 API 密钥——或者完全放弃对这些请求使用 AES 加密,并尝试通过其他方式(例如 CSRF 令牌)进行验证,并确保所有请求都来自表单我们自己的前端在某种程度上 - 但如果我们想创建一个允许与其他页面或服务集成的 API,那么这将不起作用,即使这样,我将如何保护客户端的秘密会话密钥?
文章建议生成一次性 cookie 作为标记,但这是一个有限的解决方案,适用于 poster 的服务,但不适用于我们。我希望 HMAC 能够对用户发送的每个请求都使用一个可以过期和重置的用户特定密钥,并且由于该服务最终将处理资金,我希望请求身份验证被严格锁定。
那么我有什么选择呢?
因为它 is doomed,我就放弃 Javascript 了吗?有什么方法可以存储密钥,而不会像硬编码到 .js 脚本中那样将其公开?我是否应该生成一个新的临时密钥仅用于登录调用并在用户请求服务器随机数时将其发送给用户?
另外,我链接的post首先建议使用cookie来存储客户端的会话密钥,然后从JS访问该密钥。这样可以吗,还是会提供比密封更多的孔?
很高兴知道哪些措施可以防止哪些安全漏洞。
你说得对,JavaScript 不适合加密,因为没有地方可以存储秘密。也没有好的加密库,因为你不应该在 JavaScript.
中进行加密
会话密钥可以作为身份验证密钥。如果您使用的是 TLS,您的连接是安全的,攻击者无法知道会话密钥。
此外,JavaScript 不需要知道会话密钥。默认情况下,每次请求都会发送 Cookie。您可以将 cookie 设置为仅限 http 的 cookie。您 不必 这样做,但它确实增加了另一层安全性。
您可以给会话 cookie 一个很长的过期时间,这样它基本上就像一个秘密 API 密钥一样工作。浏览器将负责安全地存储 cookie。建议经常轮换会话密钥,通常是在每个新会话开始时和身份验证信息更改时(如密码重置)。
CSRF 令牌防止重放攻击。绝对建议使用 CSRF 令牌保护修改请求。您不需要对每个请求进行 CSRF 检查,只需请求修改敏感信息(例如您的登录凭据,或者在您的情况下:交易)。
对于 CSRF 令牌,您可以使用与会话密钥相同的方法:将其存储在 cookie 中。
关键是 JavaScript 不需要知道这些。
我相信您也意识到了一件重要的事情,即您生成的任何密钥或随机数都必须是加密安全的。不要使用低熵函数。
所以:
您不需要加密用户 ID 或电子邮件,TLS 已经为您完成了。另外你也可以发送密码,你不需要在第3步单独发送。我们不会在JavaScript中做任何加密。所有加密都由 TLS/HTTPS 单独处理。
如果您有单独的身份验证服务器(如单点登录),这种方法很好。否则你可以跳过这一步。
你不需要这个。
服务器不需要解密任何东西,加密由 TLS 处理。如何存储密码本身就是一个话题,但我想您已经知道了。
好的。同样,客户端不应加密任何内容。
只发送会话密钥。够了
修改为:
客户端发送登录凭据。连接必须安全。
服务器验证凭据并将身份验证令牌作为 cookie 发送,并通过会话列表跟踪身份验证令牌。
对于每个请求:
客户端包含身份验证令牌。如果您使用 cookie,这会自动发生。
服务器验证身份验证令牌并可能生成一个新的令牌供客户端使用。
移动应用应被视为 public clients。这意味着他们不应该存储任何秘密。无论您将使用哪种加密算法,都无法阻止客户端凭据被泄露。
这就是为什么 OAuth2 框架协议定义 Implicit grant type flow which allow public client interaction and do not need any client authentication. You may also consider the RFC7636 来保护访问令牌的发布。
我们正在迁移基于 MVC 的服务器应用程序并制作一个 REST-ful API 来处理调用。
我一直在阅读 AES 加密和 OAuth2,并决定实施一个从这些概念中成长起来的解决方案,如下所示:
- 客户端发送请求以提供用户 ID 或电子邮件登录。此请求是使用 API 密钥的 HMAC。
- 服务器检查 UserID/Email 是否与现有帐户匹配,如果找到,则创建并存储一个服务器随机数,将其作为响应的一部分发送给客户端。
- 客户端创建自己的客户端随机数,并根据 API 密钥和两个随机数创建一个新的临时密钥。然后它发送一个登录请求,其中包含使用此临时密钥加密的密码 [为了增加熵并避免以明文形式发送密码]。
- 服务器使用为该客户端存储在该平台上的最新随机数 [移动和 Web 客户端可以有自己不同的随机数和会话] 以及在清楚,如果 HMAC 检出它然后根据数据库验证密码 [PBKDF2 散列和加盐]。
- 如果请求有效且密码和用户 ID 匹配记录,则在该平台上为该用户 ID 创建一个新的会话密钥,并将此密钥发送到客户端,并将每隔 [=47] 用于 HMAC =] 从今以后请求来自帽子客户端。
- 任何新的非登录请求都将包含从会话密钥和随机 IV 计算得出的 HMAC 签名。
所有通信都通过 TLS 处理,因此这是增加的安全性,而不是唯一的防线。
在移动应用程序上,这会起作用,因为您可以在配置文件中隐藏移动应用程序的密钥,这提供了一些不错的安全措施 - [也许不是很多,我不完全确定]但如果我们尝试将我们网页上的所有请求转换为这种形式,这意味着使用 Javascript 来处理客户端 AES 加密和身份验证以及... well as this article clearly explains,“如果您存储 API在 JavaScript 网络应用程序中键入您还不如在主页上用粗体字打印出来,因为现在全世界都可以通过浏览器的开发工具访问它。"
我只能使用随机数作为 API 密钥——或者完全放弃对这些请求使用 AES 加密,并尝试通过其他方式(例如 CSRF 令牌)进行验证,并确保所有请求都来自表单我们自己的前端在某种程度上 - 但如果我们想创建一个允许与其他页面或服务集成的 API,那么这将不起作用,即使这样,我将如何保护客户端的秘密会话密钥?
文章建议生成一次性 cookie 作为标记,但这是一个有限的解决方案,适用于 poster 的服务,但不适用于我们。我希望 HMAC 能够对用户发送的每个请求都使用一个可以过期和重置的用户特定密钥,并且由于该服务最终将处理资金,我希望请求身份验证被严格锁定。
那么我有什么选择呢?
因为它 is doomed,我就放弃 Javascript 了吗?有什么方法可以存储密钥,而不会像硬编码到 .js 脚本中那样将其公开?我是否应该生成一个新的临时密钥仅用于登录调用并在用户请求服务器随机数时将其发送给用户?
另外,我链接的post首先建议使用cookie来存储客户端的会话密钥,然后从JS访问该密钥。这样可以吗,还是会提供比密封更多的孔?
很高兴知道哪些措施可以防止哪些安全漏洞。
你说得对,JavaScript 不适合加密,因为没有地方可以存储秘密。也没有好的加密库,因为你不应该在 JavaScript.
中进行加密会话密钥可以作为身份验证密钥。如果您使用的是 TLS,您的连接是安全的,攻击者无法知道会话密钥。 此外,JavaScript 不需要知道会话密钥。默认情况下,每次请求都会发送 Cookie。您可以将 cookie 设置为仅限 http 的 cookie。您 不必 这样做,但它确实增加了另一层安全性。
您可以给会话 cookie 一个很长的过期时间,这样它基本上就像一个秘密 API 密钥一样工作。浏览器将负责安全地存储 cookie。建议经常轮换会话密钥,通常是在每个新会话开始时和身份验证信息更改时(如密码重置)。
CSRF 令牌防止重放攻击。绝对建议使用 CSRF 令牌保护修改请求。您不需要对每个请求进行 CSRF 检查,只需请求修改敏感信息(例如您的登录凭据,或者在您的情况下:交易)。 对于 CSRF 令牌,您可以使用与会话密钥相同的方法:将其存储在 cookie 中。
关键是 JavaScript 不需要知道这些。
我相信您也意识到了一件重要的事情,即您生成的任何密钥或随机数都必须是加密安全的。不要使用低熵函数。
所以:
您不需要加密用户 ID 或电子邮件,TLS 已经为您完成了。另外你也可以发送密码,你不需要在第3步单独发送。我们不会在JavaScript中做任何加密。所有加密都由 TLS/HTTPS 单独处理。
如果您有单独的身份验证服务器(如单点登录),这种方法很好。否则你可以跳过这一步。
你不需要这个。
服务器不需要解密任何东西,加密由 TLS 处理。如何存储密码本身就是一个话题,但我想您已经知道了。
好的。同样,客户端不应加密任何内容。
只发送会话密钥。够了
修改为:
客户端发送登录凭据。连接必须安全。
服务器验证凭据并将身份验证令牌作为 cookie 发送,并通过会话列表跟踪身份验证令牌。
对于每个请求:
客户端包含身份验证令牌。如果您使用 cookie,这会自动发生。
服务器验证身份验证令牌并可能生成一个新的令牌供客户端使用。
移动应用应被视为 public clients。这意味着他们不应该存储任何秘密。无论您将使用哪种加密算法,都无法阻止客户端凭据被泄露。
这就是为什么 OAuth2 框架协议定义 Implicit grant type flow which allow public client interaction and do not need any client authentication. You may also consider the RFC7636 来保护访问令牌的发布。