OAuth 2 中隐式授权类型的目的是啥?

Posted

技术标签:

【中文标题】OAuth 2 中隐式授权类型的目的是啥?【英文标题】:What is the purpose of the implicit grant authorization type in OAuth 2?OAuth 2 中隐式授权类型的目的是什么? 【发布时间】:2011-11-23 06:47:26 【问题描述】:

我不知道我是否只是有某种盲点或什么,但我已经多次阅读 OAuth 2 规范并仔细阅读邮件列表档案,但我还没有找到一个很好的解释为什么已经开发了用于获取访问令牌的隐式授予流程。与授权码授予相比,它似乎只是无缘无故地放弃了客户端身份验证。这是如何“针对使用脚本语言在浏览器中实现的客户端进行优化”(引用规范)?

两个流程开始时相同(来源:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-22):

    客户端通过将资源所有者的用户代理定向到授权端点来启动流程。 授权服务器验证资源所有者(通过用户代理)并确定资源所有者是允许还是拒绝客户端的访问请求。 假设资源所有者授予访问权限,授权服务器使用之前提供的重定向 URI(在请求中或在客户端注册期间)将用户代理重定向回客户端。
重定向 URI 包含授权码(授权码流) 重定向 URI 在 URI 片段中包含访问令牌(隐式流)

这里是分流的地方。在这两种情况下,此时的重定向 URI 都指向客户端托管的某个端点:

在授权代码流中,当用户代理使用 URI 中的授权代码访问该端点时,该端点上的代码会交换授权代码及其客户端凭据以获取访问令牌,然后它可以根据需要使用该令牌。例如,它可以将其写入网页上的脚本可以访问的网页。 隐式流程完全跳过此客户端身份验证步骤,仅加载带有客户端脚本的网页。 URL 片段有一个可爱的技巧,可以防止访问令牌被过多地传递,但最终结果基本上是相同的:客户端托管的站点提供一个页面,其中包含一些可以获取访问令牌的脚本.

因此我的问题是:跳过客户端身份验证步骤在这里获得了什么?

【问题讨论】:

看看这个:ibm.com/developerworks/wikis/display/… 上一条评论中的链接已失效。 Here's an updated one 我已经阅读了这里的所有答案,但我仍然不明白不需要私人客户端密码来获取访问令牌如何是安全的。假设 TrustedAppDeveloper 发布了 TrustedPopularApp,让用户使用隐式授权授予它权限(比如使用 Twitter oauth)。如果我是 EvilAppDeveloper,那么是什么阻止我制作一个应用程序,该应用程序在隐式授权请求中将 TrustedPopularAppId 作为 client_id 传递,然后代表用户执行操作(如发送垃圾邮件),现在看起来它们来自 TrustedPopularApp ? @adevine 阻止您场景中的 EvilApp 作为 TrustedPopularApp 向 Twitter 进行身份验证的原因是它无法接收来自 Twitter 的回调,它们将始终被发送到注册时定义的 URI客户编号 What is the difference between the 2 workflows? When to use Authorization Code flow?的可能重复 【参考方案1】:

以下是我的想法:

授权码流中授权码+令牌的目的是令牌和客户端密码永远不会暴露给资源所有者,因为它们在服务器到服务器之间传输。

另一方面,隐式授权流适用于完全使用 javascript 实现并在资源所有者的浏览器中运行的客户端。您不需要任何服务器端代码即可使用此流程。然后,如果一切都发生在资源所有者的浏览器中,那么发布验证码和客户端密码就没有意义了,因为令牌和客户端密码仍将与资源所有者共享。包含身份验证代码和客户端密码只会使流程更加复杂,而不会增加任何真正的安全性。

那么关于“获得了什么?”的答案是什么?是“简单”。

【讨论】:

谢谢。这是一个很好的观点,在授权代码流中,资源所有者永远不需要查看访问令牌,而在 javascript 客户端中这是不可避免的。仍然可以使用授权代码流从 javascript 客户端保留客户端机密,但是:在验证并获得访问令牌之后,服务器端代码会将令牌传递给 javascript 客户端。不过,我现在看到的是,隐式授权流支持分发 javascript oauth SDK,如 Facebook 的,让开发人员不必完全编写自己的 oauth 代码。 我可能会补充一点,授权代码流使客户端能够存储令牌并重用它们。在隐式流程中,您并不总是拥有该选项,因此,隐式流程是安全性和便利性之间的务实选择。 这只回答了一半,“丢了什么”? 我不认为这是一个全面的答案,隐式流程并不是为了在简单性上获得优势,而是为了妥协客户端应用程序的安全问题。 Auth codeclient_idclient_secret 一起用于识别可以刷新令牌以进行长时间登录和"offline login" 的可信客户端。然而在客户端应用程序中,没有办法注册每个客户端,因此“简化”的隐式授权类型用于临时访问用户信息 包含客户端密码不仅会使流程更加复杂,还会使其安全性降低。如果需要在客户端代码中枚举客户端机密,则它不是机密,因此它会暴露在互联网上。如果您的客户端 ID 仅用于隐式流,这不是问题。但是,如果它还在您平台的其他地方用于刷新令牌或授权代码授予,那么暴露相应的秘密是一个大问题。【参考方案2】:

出于安全原因,而不是为了简单。

您应该考虑用户代理客户端之间的区别:

用户代理是用户(“资源所有者”)与系统的其他部分(身份验证服务器和资源服务器)进行通信的软件。

客户端是要访问资源服务器上用户资源的软件。

在分离用户代理和客户端的情况下,授权码授予是有意义的。例如。用户使用网络浏览器(用户代理)在 Kickstarter 上使用他的 Facebook 帐户登录。在这种情况下,客户端是 Kickstarter 的服务器之一,它处理用户登录。此服务器从 Facebook 获取访问令牌和刷新令牌。因此这种类型的客户端被认为是“安全的”,由于访问受限,令牌可以被保存,Kickstarter 可以访问用户的资源,甚至可以在没有用户交互的情况下刷新访问令牌。

如果用户代理和客户端是耦合的(例如本地移动应用程序、javascript 应用程序),则可以应用隐式授权工作流程。它依赖于资源所有者的存在(用于输入凭据)并且不支持刷新令牌。如果此客户端存储访问令牌以供以后使用,这将是一个安全问题,因为该令牌可以很容易地被客户端的其他应用程序或用户提取。没有刷新令牌是一个额外的提示,这个方法不是为在用户不在的情况下访问用户资源而设计的。

【讨论】:

我看到我的浏览器已经登录我的谷歌帐户几个月了。那么谷歌是在浏览器上使用访问令牌还是使用过期时间长的访问令牌?过期时间长的访问令牌和访问令牌在使用上有什么区别?任何其他客户端都可以捕获访问令牌并在资源所有者不存在时使用它。 我假设您的意思是刷新令牌过期时间长的访问令牌之间的区别?刷新令牌不应保存在不安全的场景中,但您可以保存访问令牌(例如,在浏览器的本地存储中)。安全性是通过尽可能短的访问令牌的生命周期来实现的,尽管对您的用户来说仍然很舒服(例如,您可以在 x 分钟不活动后自动注销它们)。如果您使用长寿命访问令牌,您实际上会使刷新令牌过时。 感谢您的解释,但我也有另一个困惑。我不明白为什么我们需要“授权码”流程。我们可以通过隐式流(access_token)和刷新令牌在服务器上达到相同的结果。看起来隐式流的唯一安全考虑是 access_code 的寿命应该很短,因此它不能在服务器到服务器上使用。好的,但是刷新令牌解决了这个问题。为什么我们应该使用 auth_code 流程并通过服务器上的令牌请求 access_token 来获取 access_code 而我们可以使用 refresh_token 达到相同的结果? "token可以被其他应用轻松提取"如何? @MohammadNikravan 在***.com/q/13387698/355438寻找答案【参考方案3】:

通常的解释是,当您使用 JavaScript 客户端时,隐式授权更容易实现。但我认为这是错误的看待它的方式。如果您使用的 JavaScript 客户端直接通过 XMLHttpRequest 请求受保护的资源,则隐式授权是您唯一的选择,尽管它的安全性较低。*

授权码授予提供了额外的安全性,但它仅在您拥有请求受保护资源的 Web 服务器时才有效。由于 Web 服务器可以存储访问令牌,因此访问令牌暴露于 Internet 的风险较小,并且您可以发出一个持续很长时间的令牌。并且由于网络服务器是可信的,它可以被赋予一个“刷新令牌”,所以当旧的令牌过期时它可以得到一个新的访问令牌。

但是——这是很容易忽略的一点——授权代码流的安全性只有在网络服务器受到会话保护的情况下才有效,会话是通过用户身份验证(登录)建立的。如果没有会话,不受信任的用户只能使用 client_id 向 Web 服务器发出请求,这就像用户拥有访问令牌一样。添加会话意味着只有经过身份验证的用户才能访问受保护的资源。 client_id 只是 JS webapp 的“身份”,不是该 webapp 的身份验证。

这也意味着您可以在 OAuth 令牌过期之前结束会话。没有使访问令牌无效的标准方法。但是如果你的会话过期了,访问令牌就没有用了,因为除了 Web 服务器之外没有人知道它。如果不受信任的用户获得了您的会话密钥的访问权限,那么他们将只能在会话有效期间访问受保护的资源。

如果没有网络服务器,您必须使用隐式授权。但这意味着访问令牌会暴露在 Internet 上。如果不受信任的用户可以访问它,他们可以使用它直到它过期。这意味着他们可以访问它的时间比授权码授予的时间长。因此,您可能需要考虑让令牌更快过期,并避免授予对更敏感资源的访问权限。

*编辑:最近,人们建议您避免使用隐式授权,即使在没有服务器的网络应用上也是如此。相反,您可以使用配置了空密钥的授权代码授权以及 PKCE。 auth-code grant 避免了将访问令牌存储在浏览器历史记录中,并且 PKCE 避免在有人劫持重定向 URL 以窃取 auth 代码时暴露它。在这种情况下,您需要服务器避免返回刷新令牌,因为您的客户端可能无法安全地存储它。它应该发出具有上述相同限制的访问令牌。

【讨论】:

【参考方案4】:

归结为:如果用户正在运行基于浏览器的或“公共”(JavaScript) Web 应用程序而没有服务器端组件,则用户隐式信任该应用程序(并且它运行的浏览器,可能与其他基于浏览器的应用程序...)。

没有第三方远程服务器,只有资源服务器。授权代码没有任何好处,因为除了浏览器代表用户之外,没有其他代理。出于同样的原因,客户端凭据没有任何好处。 (任何客户端都可以尝试使用此流程。)

然而,安全隐患是显着的。来自https://www.rfc-editor.org/rfc/rfc6749#section-10.3:

当使用隐式授权类型时,访问令牌在 URI 片段,这可能会将其暴露给未经授权的各方。

来自https://www.rfc-editor.org/rfc/rfc6749#section-10.16:

资源所有者可以通过以下方式自愿委托对资源的访问权限 向攻击者的恶意客户端授予访问令牌。这可能 是由于网络钓鱼或其他一些借口...

【讨论】:

没有服务器端组件的“公共”(JavaScript)Web 应用程序是什么意思?没有服务器怎么会有web应用? @ZammyPage,这就是通常所说的单页应用程序 (SPA)。整个应用程序由静态资源提供。然后,应用程序中的 Javascript 在它可以访问的任何资源服务器上动态访问它需要的任何资源。没有生成客户端内容的服务器:客户端中的 javascript 根据需要修改 DOM 以表示它访问过的资源。 有一个简单但有意义的好处:如果您存储服务器日志并且您正在使用授权代码流,那么如果日志泄漏,所有代码都将很可能无效。如果您正在存储访问令牌,但您可以直接模拟用户会话。【参考方案5】:

我不确定我是否正确理解了答案和 Dan 的评论。在我看来,答案已经说明了一些正确的事实,但它确实指出了 OP 的要求。如果我理解正确,隐式授权流的主要优点是像 JS 应用程序(例如 Chrome 扩展)这样的客户端不必公开客户端密码。

丹·塔夫林说:

...在授权代码流中,资源所有者永远不需要查看访问令牌,而在 javascript 客户端中,这是不可避免的。但是,仍然可以使用授权代码流对 javascript 客户端保留客户端机密......

也许我误解了你,但是客户端(在这种情况下是 JS 应用程序)必须在授权代码流中将客户端凭据(客户端密钥和秘密)传递给资源服务器,对吧?客户端机密不能“对 JS 保密”。

【讨论】:

我意识到这是一个老问题,但这是一个比公认的更好的答案。 Implicit Grant 存在的原因是 javascript 客户端无法保密,因此无法进行身份验证。因此,授权服务器必须依赖重定向 uri 注册和用户代理以确保安全。您仅将授权令牌传递给用户代理,并且仅在特定的重定向 uri 处传递,理论上可以防止拦截(因为不拥有重定向 uri 域的恶意用户无法在该 uri 的用户代理中执行代码)。跨度> 确实接受的答案让我感到困惑。让我觉得我误解了 client_secret 是什么!这个答案和上面的评论是正确的。【参考方案6】:

虽然 Implicit Grant 旨在支持无法保护客户端密码的应用程序(包括客户端 JavaScript 应用程序),但一些提供商正在实施替代方案,使用授权代码而不使用客户端密码。 OAuth 2.0 IETF RFC-6749 于 2012 年发布,目前一些最近讨论的建议来自 2017 年。

2017 年关于 IETF OAuth 邮件列表的讨论可从这些实施者处获得:

红帽:https://www.ietf.org/.../oauth/current/msg16966.html 德国电信:https://www.ietf.org/.../oauth/current/msg16968.html 智慧健康IT:https://www.ietf.org/.../oauth/current/msg16967.html

在这里阅读更多:

https://aaronparecki.com/oauth-2-simplified/ https://aaronparecki.com/oauth-2-simplified/#single-page-apps

隐式以前曾推荐给没有秘密的客户,但已被使用无秘密的授权代码授予所取代。

...

以前,建议基于浏览器的应用使用“隐式”流程,该流程会立即返回访问令牌,并且没有令牌交换步骤。自最初编写规范以来,行业最佳实践已更改为建议在没有客户端密码的情况下使用授权代码流。这为创建安全流提供了更多机会,例如使用 state 参数。参考:Redhat、Deutsche Telekom、Smart Health IT。

这里还提到了移动应用程序从隐式授权中迁移到没有客户端密码的身份验证代码:

https://aaronparecki.com/oauth-2-simplified/#mobile-apps

【讨论】:

我认为你要小心这个建议。这是在本机应用程序指南中推荐的,而不是水疗中心。不幸的是,许多在线讨论、论坛甚至 oauth-wg 邮件列表中都没有关于 SPA 的良好指导。 建议在没有隐式授权的情况下转移到身份验证代码是对 SPA 和移动应用程序的建议,但我上面的摘录是针对 SPA 的。引用的文章对 SPA 和移动应用程序使用了类似的文本,但在各自的文本中使用了“基于浏览器的应用程序”“移动和本机应用程序”的语言。此外,Redhat、DT、Smart Health IT 的参考资料是特定于 SPA 的,不包含在移动应用程序的注释中。我在答案中添加了指向 SPA 的深层链接,以便更容易找到。请发布一些您提到的讨论的链接。 可以在ietf.org/mail-archive/web/oauth/current/msg18020.html 找到最近的 (2018) oauth-wg 讨论。 RFC 8252 适用于本机应用程序,因为标题建议“OAuth 2.0 for Native Apps”。对 Redhat、DT、Smart Health IT 的引用是对邮件列表讨论的回应,而不是 rfc、工作草案等......【参考方案7】:

在隐式流程中,如果用户的浏览器损坏(恶意扩展程序/病毒),则损坏会访问用户的资源并可能做坏事。

在身份验证流程中,损坏不能,因为它不知道客户端密码。

【讨论】:

【参考方案8】:

除了其他答案之外,同样重要的是要认识到隐式配置文件只允许前端通道流,而不是需要回调授权服务器的授权代码流;这在 OpenID Connect 中变得很明显,它是一个建立在 Auth 2.0 之上的 SSO 协议,其中隐式流程类似于非常流行的 SAML POST 绑定,而授权代码流程类似于不太广泛部署的 SAML Artifact 绑定

【讨论】:

【参考方案9】:

https://www.rfc-editor.org/rfc/rfc6749#page-8

隐式

隐式授权是一种简化的授权代码流程 针对使用脚本在浏览器中实现的客户端进行了优化 JavaScript 等语言。在隐式流中,而不是 向客户端发出授权码,客户端被发出 直接访问令牌(作为资源所有者的结果 授权)。授权类型是隐式的,因为没有中间 颁发凭据(例如授权代码)(以及稍后 用于获取访问令牌)。

在隐式授权流程中颁发访问令牌时, 授权服务器不对客户端进行身份验证。在一些 在这种情况下,可以通过重定向 URI 验证客户端身份 用于将访问令牌传递给客户端。访问令牌可能 暴露给资源所有者或其他有权访问的应用程序 资源所有者的用户代理。

隐式赠款提高了某些人的响应能力和效率 客户端(例如作为浏览器内应用程序实现的客户端), 因为它减少了获得一个所需的往返次数 访问令牌。

【讨论】:

【参考方案10】:

我认为 Will Cain 回答了这个问题,他说“出于同样的原因,客户端凭据没有任何好处。(任何客户端都可以尝试使用此流程。)”还考虑到用于隐式流程的 redirect_uri 可能是“localhost”- - 没有从授权服务器对隐式流进行回调。由于无法预先信任客户端,因此用户必须批准发布用户声明。

【讨论】:

【参考方案11】:

隐式授权允许使用GET 从Authorization Endpoint 获取令牌。这意味着授权服务器不必支持 CORS。

如果这不是一个问题,并且没有其他与授权服务器相关的问题不灵活(例如,出于某种原因,刷新令牌不是可选的),那么授权代码流是首选,即使对于公共客户端,根据recent industry trends 和至少这个(当前)instance of an official draft。

从历史上看,实现隐式流程还有其他原因,但目前授权代码授予提供的安全优势似乎超过了这些原因,包括:

通过反向渠道为机密客户提供和使用令牌的选项 不在公共客户端的浏览器历史记录中公开令牌 在颁发令牌之前中断未经授权的流程 - 使用PKCE,用于"all kinds of OAuth clients"

【讨论】:

【参考方案12】:

我刚刚看到一些关于 OAuth 2.0 的文章。作者指出,隐式流背后的原因是 JS 应用程序在请求中受到了很大限制:

如果您想知道为什么 OAuth 2.0 中包含隐式类型,那么 解释很简单:同源策略。那时候,前端 不允许应用程序向不同的主机发送请求以 使用代码获取访问令牌。今天我们有CORS(跨域 资源共享)。

https://medium.com/securing/what-is-going-on-with-oauth-2-0-and-why-you-should-not-use-it-for-authentication-5f47597b2611

【讨论】:

以上是关于OAuth 2 中隐式授权类型的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

scala中隐式转换之总结

C#中隐式类型本地变量var

使用 oauth2-client 作为隐式授权类型时解决 [authorization_request_not_found]

OAuth 2身份验证中grant_type参数的目的是啥

oauth2 代理边车的目的是啥?

scala中隐式转换