为什么OAuth2中有“授权码”流程,而“隐式”流程已经运作良好?

355
使用“隐式”流程,客户端(可能是浏览器)将在资源所有者(即用户)授权后获得访问令牌。
然而,“授权码”流程中,客户端(通常是Web服务器)只会在资源所有者(即用户)授权后获取授权码。 客户端随后使用该授权码调用API,并将client_id、client_secret和授权码一起传递以获取访问令牌。点击此处阅读完整说明。
两个流程都具有完全相同的结果:访问令牌。 然而,“隐式”流程更简单。
问题是:为什么还要使用“授权码”流程,当“隐式”流程似乎已经可以很好地工作? 为什么不仅在Web服务器中使用“隐式”流程? 这对提供者和客户端来说需要更多的工作。

4
请查看https://dev59.com/Q2sz5IYBdhLWcg3w_NED,了解隐式授权授权类型在OAuth 2中的目的。 - Jon Nylander
2
谢谢,我已经阅读了。但是它并没有回答我的问题。 - Aron Woost
1
其实是个好问题,很少有人回答 :) 请看下文。 - Nicolas Garnier
1
@AronWoost 我认为你误解了服务器 Web 应用程序和浏览器应用程序。 - onmyway133
显示剩余2条评论
8个回答

377

tl;dr: 这是出于安全原因。

OAuth 2.0想要满足以下两个条件:

  1. 您希望允许开发人员使用非HTTPS重定向URI,因为并非所有开发人员都拥有启用SSL的服务器,如果他们拥有,则不总是正确配置(非自签名,可信任的SSL证书,同步的服务器时钟...)。
  2. 您不希望黑客通过拦截请求来窃取访问/刷新令牌。

以下是详细信息:

由于安全原因,只有在浏览器环境下才可能出现隐式流程:

隐式流程中,访问令牌直接作为哈希片段传递(而不作为URL参数)。哈希片段的一个重要特点是,一旦您跟随包含哈希片段的链接,只有浏览器会意识到哈希片段。浏览器将直接将哈希片段传递到目标网页(重定向URI / 客户端网页)。哈希片段具有以下属性:

  • 它们不是HTTP请求的一部分,因此服务器无法读取它们,并且因此不能被中间服务器/路由器拦截(这很重要)。
  • 它们只存在于浏览器 - 客户端 - 所以读取哈希片段的唯一方法是使用在页面上运行的JavaScript。

这使得可以直接将访问令牌传递给客户端,而无需担心中间服务器拦截。但是这有一个缺点,即仅限于客户端侧,并需要客户端侧运行JavaScript才能使用访问令牌。

隐式流程还存在安全问题,需要进一步的逻辑来解决/避免,例如:

  • 攻击者可能会从另一个网站/应用程序获取用户的访问令牌(假设他是另一个网站/应用程序的所有者),在他们的网站上记录令牌,然后将其作为URL参数传递到您的网站上,从而冒充用户在您的网站上进行操作。为避免此类攻击,您需要检查与访问令牌关联的客户端ID(例如对于Google,您可以使用tokeninfo终点)以确保令牌是使用您自己的客户端ID(即由您自己的应用程序)发行的,或者如果您正在使用IDToken,则检查签名(但这需要您的客户端密钥)。
  • 如果授权请求没有来自您自己的属性(称为会话固定攻击),为了避免这种情况,您需要从您的网站生成一个随机哈希值,将其保存在cookie中,并在授权请求的状态URL参数中传递相同的哈希值,当用户返回时,检查cookie中的状态参数并确保它与哈希匹配。
  • 授权码流程中,不可能直接在URL参数中传递访问令牌,因为URL参数是HTTP请求的一部分,因此任何中间服务器/路由器(可能达到数百个)都可以读取访问令牌,如果您没有使用加密连接(HTTPS),则可能会导致中间人攻击。

    理论上,可以直接在URL参数中传递访问令牌,但是授权服务器必须确保重定向URI使用带有TLS加密的HTTPS和“受信任”的SSL证书(通常来自不免费的证书颁发机构),以确保目标服务器是合法的,HTTP请求完全加密。要求所有开发人员购买SSL证书并正确配置其域名上的SSL将是一项巨大的痛苦,并将极大地减慢采用速度。这就是为什么提供一个中介的一次性“授权码”,只有合法的接收者才能进行交换(因为需要客户端密钥),而且该代码对于截获未加密事务的潜在黑客来说是无用的(因为他们不知道客户端密钥)。

    你也可以认为隐式流程不够安全,存在潜在攻击向量,例如通过劫持客户网站的IP地址欺骗重定向域名。这是隐式流程仅授予访问令牌(应具有有限使用时间)而永远不会刷新令牌(时间无限制)的原因之一。为了解决这个问题,建议尽可能将您的网页托管在启用HTTPS的服务器上。


21
@AndyDufresne 这两个请求必须通过 HTTPS(强制要求)进行,因为它们是发往 OAuth 服务器的请求,OAuth 服务器只支持 HTTPS。仅客户端/请求服务器不需要支持 HTTPS,因此只有“认证代码”可能会在 HTTP 上明文发送。但是没有客户端 ID/密钥,“认证代码”是无用的。基本上,OAuth 代码流的目的是让 SSL 启用的服务器的负担落在 OAuth 提供者(Google/Facebook 等)身上,而不是 API 的用户(你和我)。 - Nicolas Garnier
6
好的,我现在理解了授权代码可以通过明文HTTP传递,并存在被嗅探的风险。将其变为一次性使用代码,并接受客户端密钥以交换访问令牌,授权服务器可以防止中间人攻击。但是,这是否也适用于访问令牌?由于API用户可能在明文HTTP上运行,黑客会不会有窃取访问令牌的风险?顺便说一下,我感谢您在此线程停滞一段时间后仍然努力解释概念。谢谢! - Andy Dufresne
9
API的请求 - 即发送访问令牌以授权请求时 - 也必须强制使用HTTPS进行。理论上,客户端在任何时候都不应该通过明文HTTP发送访问令牌。 - Nicolas Garnier
5
这一步中的访问令牌是客户端向资源服务器发出的HTTPS请求的响应的一部分。这个响应仍然是加密的。 - Nicolas Garnier
14
基本上,客户端向资源服务器发起的请求通过HTTPS进行(因为资源拥有者服务器必须支持HTTPS)。只有来自其他地方对客户端发起的请求可能会在HTTP上完成(因为客户端服务器可能不支持HTTPS)。例如,在用户在授权页面上授权后进行身份验证流程期间发生的重定向是由浏览器对客户端服务器发起的重定向,并且可能使用HTTP完成。 - Nicolas Garnier
显示剩余25条评论

17
隐式流程使整个流程更加简单,但也更不安全
由于客户端应用程序通常是在浏览器中运行的JavaScript,因此可信度较低,因此不会返回长期访问的刷新令牌。
您应该将此流程用于需要临时访问(几个小时)用户数据的应用程序。
将访问令牌返回给JavaScript客户端还意味着您的基于浏览器的应用程序需要特别小心-考虑可能泄露访问令牌到其他系统的XSS攻击。

https://labs.hybris.com/2012/06/05/oauth2-the-implicit-flow-aka-as-the-client-side-flow


1
我认为当一个网站存在 XSS 漏洞时,即使使用授权码流程也无法提供太多帮助。但是我同意隐式流程中访问令牌传递给 JavaScript 的方式(作为哈希片段)是标准化的,如果网站存在 XSS 漏洞,则构造攻击以从 URL 哈希片段中读取访问令牌相当容易。另一方面,使用授权码流程可能会导致跨站点请求伪造。 - Marcel
1
此外,这不仅仅涉及跨站脚本攻击。任何在您的网站上运行的JavaScript库都可能尝试窃取访问令牌(例如第三方CDN库或您的JavaScript框架使用的开源库)。 - Marcel
3
现在我们有内容安全策略头部和子资源完整性(SRI)哈希值,XSS 已经不再是一个大问题。 - Sergey Ponomarev

15

对于谷歌员工:

  • 您授权第三方访问您的Gmail联系人
  • 访问以令牌形式授予
  • 任何持有有效令牌的人都可以获得访问权限
  • 因此,您不希望暴露令牌,并尽量减少其转移
  • 隐式流程中(不受控制的)浏览器获取访问令牌,从而将令牌公开
  • 使用授权代码流程,浏览器仅获取临时授权码而不是访问令牌,而且授权码没有与第三方和Gmail独有的密码就无用

结论

  • 攻击者要想访问您的Gmail联系人必须先侵入您的第三方账户
  • 然而攻击者永远无法掌握访问令牌,因此无法直接对您的Gmail联系人执行操作
  • 您可以授权第三方访问许多服务,因此您不希望在本地计算机上存储所有重要的令牌
  • 但是,在只有前端而没有后端存储令牌的情况下,您只能使用隐式流程
  • 那么它只能依赖于前端来存储令牌,但是它对此几乎没有控制权

隐喻

  • 隐式流程:您向提供者请求一把钥匙,将其存放在您的钱包中,您有责任确保其安全,您会谨慎地直接使用该钥匙,并及时将其更换为新的钥匙。
  • 授权码模式: 你向提供者请求一个授权码, 授权码交给你的代理人(valet), 代理人将授权码和一个秘密文本组合起来并将其与提供者交换以获取密钥, 当需要使用密钥时,你请求代理人使用密钥,但你自己不会看到密钥,而你的代理人负责交换新的密钥
  • 大多数情况下,你的代理人比你更注重安全 :)
  • 当你没有代理人时,你就只能靠自己了

  • 如果有一个React浏览器应用程序和一个OAuth服务器,那么"VALLET"是什么?我猜它是后端?即使是这样,后端如何知道请求来自React应用程序而不是伪造的请求,这仍然不清楚。 - g.pickardou
    在VALET场景中,您必须有3个参与方。 - dz902
    此外,您还可以告诉代客泊车员,您只能使用原始钥匙的部分功能,这样,即使您喝醉了,代客泊车员仍然会阻止您做一些疯狂的事情。 - Sebastian Correa Zuluaica

    6
    我的回答是:你无法在web应用程序服务器上以安全和简单的方式实现Implicit flow。
    Web应用程序授权过程涉及用户交互,因此认证服务器应该在用户验证和同意后将用户的浏览器重定向回Web应用程序的目标页面(在与认证服务器交互后,我没有看到其他将用户返回到Web应用程序的方法)。
    那么令牌应该通过重定向URL传递给Web应用程序,对吗?
    如@NicolasGarnier在他的回答和评论中所解释的那样,没有办法将令牌作为URL片段传递-它将不会到达Web应用程序服务器。
    即使在HTTPS下,将令牌作为重定向URL的URL参数传递也是不安全的:如果目标页面(让它成为“问候页面”)包含资源(图像,脚本等),则浏览器将通过一系列HTTP(S)请求获取这些资源(每个请求都包含精确URL的“问候页面”,包括URL参数的 Referer HTTP头)。这是令牌泄漏的方式。
    因此,似乎没有办法在重定向URL中传递令牌。这就是为什么需要第二次调用(从认证服务器到客户端(但到哪个URL?)或从客户端到认证服务器(在授权代码流中的第二个调用))。

    6
    在"隐式"流程中,客户端(可能是浏览器)将通过浏览器重定向(GET操作)获得访问令牌。基于浏览器的通信不安全,您的客户机密钥或令牌可能会被拦截或窃取。
    在“授权码”流程中,客户端(通常是Web服务器)仅通过浏览器重定向(GET操作)获取授权码。然后,服务器通过向授权服务器进行(非浏览器)POST调用来交换此代码以获得令牌。服务器仅包括客户端秘密以用于访问令牌调用。
    注意-根据oauth最佳实践,“客户端不应使用隐式授权(响应类型“ token”)或其他发出访问权限的响应类型 授权响应中的令牌。”
    希望这能帮到你。

    5
    为了方便我们的客户,他们希望在手机上通过我们的应用程序进行身份验证一次,而不必每隔几周就要重新登录一次。使用代码流,您可以获取到访问令牌和刷新令牌。隐式流程不会提供刷新令牌。访问令牌的有效期较短,但刷新令牌的有效期最长可达90天。每当访问令牌过期时,客户端和服务器代码可以使用该刷新令牌,在幕后自动获取新的访问令牌加刷新令牌,完全不需要任何用户干预。刷新令牌只能使用一次。您不能使用隐式流程来实现这一点。如果您正在使用隐式流程,并且您的用户超过一个小时没有与应用程序交互,则当他们回来时,他们将不得不重新登录。在我们的用例中这是不可接受的,代码流支持我们安全的用例。
    这个方法有效且安全,因为可以撤销刷新令牌。如果客户说他们的手机、笔记本电脑丢失或黑客进入了他们的台式机,我们可以简单地撤销该用户的所有刷新令牌。在整个过程中,绝不会有任何个人身份信息(PII)触及我们的代码,尤其是用户的密码。
    代码流很棒,但需要更多的工作。目前MS没有处理它的Angular库,所以我不得不编写一个库。如果您有兴趣,我可以帮助您。

    我认为MSAL 2.0现在可以处理它。 - Bigeyes

    5
    OAuth规范中可知:
    4.2 隐式授权
    隐式授权类型用于获取访问令牌(它不支持刷新令牌的发放),并且针对已知操作特定重定向URI的公共客户端进行了优化。这些客户端通常使用脚本语言(如JavaScript)在浏览器中实现。
    由于这是基于重定向的流程,因此客户端必须能够与资源所有者的用户代理(通常是Web浏览器)进行交互,并且能够从授权服务器接收传入请求(通过重定向)。
    与授权代码授权类型不同,在该类型中,客户端通过授权请求的结果接收访问令牌,而不是为授权和访问令牌分别发出单独的请求。
    隐式授权类型不包括客户端身份验证,并依赖于资源所有者的存在和重定向URI的注册。由于访问令牌被编码到重定向URI中,因此可能会暴露给资源所有者和同一设备上的其他应用程序。
    我们可以考虑以下内容:
    1. 针对公共OAuth即无需注册客户端或没有自己的客户端密钥的情况。但是,授权服务器检查重定向URL已足以确保安全性。
    2. 访问令牌出现在浏览器的地址栏中,因此用户可以复制该URL并将其发送给其他人,并且它还会被记录为用户,这类似于会话固定。但是,浏览器会进行附加重定向,并替换历史记录以从URL中删除哈希片段。黑客通过嗅探HTTP流量窃取访问令牌也是可能的,但是这可以通过HTTPS轻松保护。一些恶意浏览器扩展程序可以访问地址栏中的URL,但这绝对是糟糕的情况,如损坏的HTTPS证书。即使是授权代码流程也无法帮助这种情况,所以我认为通过URL的哈希片段传递访问令牌是绝对安全的。
    3. 在使用HTTPS时,短暂访问令牌和刷新令牌的分离是无用的,而且即使在裸HTTP上也不太有用。但是,客户端通过隐式流程无法接收刷新令牌也是无意义的。
    因此,我认为我们应该引入一个新的授权流“安全隐式授权”,它严格基于HTTPS工作,允许刷新令牌(或者我们应该彻底摆脱它们),并且比授权代码授权流程更可取。

    0
    谈到授权代码授权类型,我们可以通过删除客户端(用户代理或网站)对最终资源的特权访问来提高安全性,其中客户端(网站所有者)假装是您使用您的授权代码,并且避免黑客使用XSS在您的浏览器上进行CRSF(网站漏洞),如果使用隐式方法可能会发生这种情况。
    关键因素是客户端ID,它在第一个请求中发送到Auth服务器。您可以将Auth Code步骤视为签名验证。
    此外,即使在完成获取访问令牌的授权代码步骤后,访问令牌最终也会落入客户端手中。此时,不再需要使用Auth服务器进行签名验证的客户端ID。因此,我不确定授权代码方法是否也完全安全(来自客户端本身)。这就是为什么您在提供登录凭据后,仍然看到Auth服务器要求您同意的原因。这意味着您信任客户端使用您的访问令牌。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接