如何在使用 OAuth2 的资源所有者密码凭据授予类型时对客户端凭据保密

Posted

技术标签:

【中文标题】如何在使用 OAuth2 的资源所有者密码凭据授予类型时对客户端凭据保密【英文标题】:How to keep the client credentials confidential, while using OAuth2's Resource Owner Password Credentials grant type 【发布时间】:2011-09-05 15:09:11 【问题描述】:

我们正在构建一个休息服务,我们想使用 OAauth 2 进行授权。 current draft(5 月 19 日的 v2-16)描述了 four grant types。它们是获取授权(访问令牌)的机制或流程。

    授权码 隐式授予 资源所有者凭据 客户端凭据

看来我们需要支持所有这四个,因为它们有不同的用途。前两个(也可能是最后一个)可以从需要访问 API 的第三方应用程序中使用。授权代码是授权 Web 应用程序的标准方法,该应用程序幸运地驻留在安全服务器上,而隐式授权流将是不能完全保密其凭据的客户端应用程序的选择(例如移动/桌面应用程序、javascript 客户端等)。 我们希望自己使用第三种机制来在移动设备上提供更好的用户体验——而不是将用户带到 Web 浏览器中的登录对话框等,用户只需直接在应用程序中输入他或她的用户名和密码并登录。 我们还想使用 Client Credentials 授权类型来获取可用于查看公共数据的访问令牌,不与任何用户关联。在这种情况下,这与其说是授权,不如说是类似于 API 密钥的东西,我们使用它来仅授予已向我们注册的应用程序的访问权限,让我们可以在需要时撤销访问权限。

所以我的问题是:

    您认为我正确理解了不同资助类型的目的吗? 如何对客户凭据保密?在第三种和第四种情况下,我们都需要在客户端的某个位置提供客户端 ID 和客户端密码,这听起来不是一个好主意。 即使您使用隐式授权类型并且不公开您的客户端密码,有什么可以阻止另一个应用程序使用相同的授权机制和您的客户端 ID 来模拟您的应用程序?

总而言之,我们希望能够使用来自客户端应用程序的客户端凭据和资源所有者凭据流。这两种流程都需要您以某种方式存储客户端密码,但客户端是移动应用程序或 JavaScript 应用程序,因此很容易被窃取。

【问题讨论】:

【参考方案1】:

我面临着类似的问题,而且对 OAuth 也比较陌生。我已经在我们的 API 中实现了“资源所有者密码凭据”,供我们的官方移动应用程序使用——Web 流看起来就像在移动平台上使用它们一样可怕,一旦用户安装了应用程序并信任这是我们的官方应用程序,他们应该会觉得直接在应用程序中输入用户名/密码很舒服。

问题是,正如您所指出的,我的 API 服务器无法安全地验证应用程序的 client_id。如果我在应用程序代码/包中包含 client_secret,那么它会暴露给任何安装该应用程序的人,因此要求 client_secret 不会使该过程更加安全。所以基本上,任何其他应用程序都可以通过复制 client_id 来模拟我的应用程序。

只是为了直接回答你的每一个问题:

    我不断重新阅读规范的不同草稿以查看是否有任何更改,并且主要关注资源所有者密码凭据部分,但我认为您在这些方面是正确的。 Client Credentials(4) 我认为内部或第三方服务也可以使用这些服务,这些服务可能需要访问的不仅仅是“公共”信息,比如您可能有分析或需要获取所有用户信息的东西。

    我认为您不能对客户端保密。

    没有什么能阻止其他人使用您的客户端 ID。这也是我的问题。一旦您的代码离开服务器并作为应用程序安装或在浏览器中作为 Javascript 运行,您就不能假设任何事情都是秘密的。

对于我们的网站,我们遇到了与您描述的客户凭据流程类似的问题。我最终做的是将身份验证移至服务器端。用户可以使用我们的 Web 应用程序进行身份验证,但我们 API 的 OAuth 令牌存储在服务器端,并与用户的 Web 会话相关联。 Javascript 代码发出的所有 API 请求实际上都是对 Web 服务器的 AJAX 调用。所以浏览器没有直接通过 API 进行身份验证,而是有一个经过身份验证的 Web 会话。

您的客户端凭据的用例似乎有所不同,因为您在谈论第三方应用程序,并且仅通过此方法提供公共数据。我认为您的担忧是有道理的(任何人都可以窃取和使用其他人的 API 密钥),但如果您只需要免费注册即可获得 API 密钥,我不明白为什么有人真的想窃取一个。

您可以监控/分析每个 API 密钥的使用情况以尝试检测滥用情况,此时您可以使一个 API 密钥失效并为合法用户提供一个新的。这可能是最好的选择,但绝不安全。

如果你想把它锁得更紧,你也可以使用类似 Refresh Token 的方案,虽然我不知道你会真正获得多少。如果您每天使 Javascript 公开的 api 令牌过期一次,并要求第三方使用(秘密)刷新令牌进行某种服务器端刷新,那么被盗的 api 令牌将永远不会超过一天。可能会鼓励潜在的令牌窃贼只注册。但对其他人来说有点痛苦,所以不确定这是否值得。

【讨论】:

我做了更多的研究,我认为你是对的——没有办法对客户保密。正如您所建议的,我们保护 API 免受滥用的最佳选择似乎是实施某种使用监控。感谢您的回答! 如果您以后遇到更好的解决方案,请告诉我! 只是为了理解,以下说法正确吗? * 当您使用经过身份验证的 Web 会话(通过 cookie 左右)并在服务器上进行“旧方式”身份验证时,与使用 OAuth2 的资源所有者密码凭据授予类型相比,没有更多的安全性,因为这也是经典的 Web 会话/cookie可能被传递给其他演员/可能被盗。 你们有没有找到解决这些问题的方法?似乎只要我打开“资源所有者凭据”(最佳移动用户体验)流程,任何应用程序都可以使用该流程,而我的身份验证服务器将无法知道第一方和第三方应用程序之间的区别. 我想知道 Facebook、Twitter 和其他规模较小但规模很大的社交网络公司如何在他们的移动应用程序中处理这个问题......

以上是关于如何在使用 OAuth2 的资源所有者密码凭据授予类型时对客户端凭据保密的主要内容,如果未能解决你的问题,请参考以下文章

OAuth 2.0 中资源所有者密码凭据授予类型的用途是啥?

OAuth2 密码授予与 OpenID 连接

如何获取 Spring Boot 和 OAuth2 示例以使用默认密码授予凭据以外的其他凭据

.NET Core 3.1 IdentityServer4:使用资源所有者密码凭据授予时获取无效访问令牌

Grails OAuth2 登录密码凭据授予返回 invalid_client

如何使用密码授予在 Spring Boot Oauth2 资源服务器中处理 CORS