【问题标题】:Why is there an "Authorization Code" flow in OAuth2 when "Implicit" flow works so well?当“隐式”流程运行良好时,为什么 OAuth2 中有“授权码”流程?
【发布时间】:2012-11-03 11:21:08
【问题描述】:

通过“隐式”流程,在资源所有者(即用户)授予访问权限后,客户端(可能是浏览器)将获得访问令牌。

但是,通过“授权码”流程,客户端(通常是 Web 服务器)只有在资源所有者(即用户)授予访问权限后才能获得授权码。然后,客户端使用该授权代码再次调用 API,将 client_id 和 client_secret 与授权代码一起传递以获取访问令牌。 All well described here.

两个流程都有完全相同的结果:访问令牌。但是,“隐式”流程要简单得多。

问题:为什么要打扰“授权码”流程,而“隐式”流程接缝就可以了?为什么不对网络服务器也使用“隐式”?

这对提供者和客户来说都是更多的工作。

【问题讨论】:

  • 谢谢,已经阅读。但没有回答问题。
  • 好问题,但很少有人回答:)见下文。
  • @AronWoost 我认为你误解了服务器网络应用程序和浏览器应用程序
  • @entropy 那是我的问题;为什么不将浏览器流也用于服务器。

标签: authentication oauth oauth-2.0


【解决方案1】:

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

OAuth 2.0 想要满足这两个标准:

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

详情如下:

出于安全原因,只能在浏览器环境中使用隐式流:

隐式流中,访问令牌直接作为散列片段(而不是作为 URL 参数)传递。关于散列片段的一件重要事情是,一旦您点击包含散列片段的链接,只有浏览器知道散列片段。浏览器会将哈希片段直接传递到目标网页(重定向 URI / 客户端网页)。哈希片段具有以下属性:

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

这使得将访问令牌直接传递给客户端成为可能,而不会有被中间服务器拦截的风险。这有一个警告,即仅可能是客户端,并且需要运行客户端的 javascript 才能使用访问令牌。

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

  • 攻击者可以从不同网站/应用程序上的用户获取访问令牌(假设他是其他网站/应用程序的所有者),在他们的网站上记录令牌,然后将其作为 URL 参数传递在您的网站上,因此在您的网站上冒充用户。为避免这种情况,您需要检查与访问令牌关联的客户端 ID(例如,对于 Google,您可以使用 tokeninfo 端点)以确保令牌是使用您自己的客户端 ID(即由您自己的应用程序)颁发的或检查签名如果您使用的是 IDToken(但这需要您的客户端密码)。
  • 如果身份验证请求不是来自您自己的财产(称为会话固定攻击),为避免这种情况,您需要从您的网站生成随机散列,将其保存在 cookie 中并在状态中传递相同的散列auth 请求的 URL 参数,当用户返回时,您使用 cookie 检查状态参数,它必须匹配。

授权代码流中,不能直接在 URL 参数中传递访问令牌,因为 URL 参数是 HTTP 请求的一部分,因此任何中间服务器/路由器都会通过它来发送请求如果您不使用允许所谓的中间人攻击的加密连接 (HTTPS),pass(可能是数百个)可能能够读取访问令牌。

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

您还可以争辩说隐式流的安全性较低,存在潜在的攻击媒介,例如在重定向时欺骗域 - 例如通过劫持客户网站的 IP 地址。这就是为什么隐式流只授予访问令牌(应该有有限的时间使用)并且从不刷新令牌(时间是无限的)的原因之一。为了解决这个问题,我建议您尽可能将网页托管在支持 HTTPS 的服务器上。

【讨论】:

  • @AndyDufresne 这两个请求必须通过 HTTPS(强制)完成,因为它们是必须仅支持 HTTPS 的 OAuth 服务器的请求。只有客户端/请求服务器不需要支持 HTTPS,所以只有 Auth Code 可能通过 HTTP 明文发送。但是如果没有客户端 ID/Secret,Auth Code 将毫无用处。基本上,OAuth 代码流程的重点在于拥有启用 SSL 的服务器的负担在于 OAuth 提供者(Google/Facebook 等),而不是 API 的用户(你、我)。
  • 好的,我现在知道身份验证代码可以通过普通 HTTP 传递,并且有被嗅探的风险。使其成为一次性使用代码并接受客户端密码以将其交换为访问令牌,授权服务器可以防止中间人攻击。但这不是也适用于访问令牌吗?由于 API 的用户可以使用普通的 HTTP,是否存在访问令牌被黑客嗅探的风险? P.S - 我很感激你在解释这个概念时所做的努力,即使这个帖子已经有一段时间了。谢谢!
  • no pb :) 对 API 的请求(即通过网络发送访问令牌(以授权请求)时)也强制通过 HTTPS 完成。从理论上讲,客户端在任何时候都不应该在纯 HTTP 中通过网络发送访问令牌。
  • 本步骤中的Access Token是Client对资源服务器的HTTPS请求响应的一部分。此响应仍处于加密状态。
  • 基本上从客户端向资源服务器发起的请求都是通过HTTPS完成的(因为资源所有者服务器必须支持HTTPS)。只有从其他地方向客户端发起的请求才能通过 HTTP 完成(因为客户端服务器可能不支持 HTTPS)。例如,在用户在 gant 页面上授予授权后,在身份验证流程期间发生的重定向是从浏览器发起到客户端服务器的重定向,并且可以在 HTTP 中完成。
【解决方案2】:

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

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

【讨论】:

  • 我希望当一个人有 XSS 漏洞时,即使是授权代码流也无济于事。但我同意,由于在隐式流中将访问令牌传递给 javascript 的方式是标准化的(作为哈希片段),并且如果网站中存在 XSS 漏洞,则构造一个从 URL 哈希读取访问令牌的攻击片段很容易。另一方面,使用授权码流,跨站点请求伪造是可能的。
  • 此外,它不仅仅是关于跨站点脚本。您网站中运行的任何 JavaScript 库都可能试图窃取访问令牌(例如第三方 CDN 库或您的 JavaScript 框架使用的开源库)。
  • 当我们拥有内容安全策略标头和子资源完整性 (SRI) 哈希值时,XSS 现在不是大问题了。
【解决方案3】:

对于 Google 员工:

  • 您将 Gmail 联系人的访问权限授予第 3 方
  • 以令牌的形式授予访问权限
  • 拥有有效令牌的任何人都可以访问
  • 因此您不想暴露令牌,并尽量减少其传输
  • 通过隐式流(不受控制的)浏览器获取访问令牌,从而将令牌公开
  • 使用身份验证代码流,浏览器只会获得临时身份验证代码,但不会获得访问令牌,而且身份验证代码在没有只有第 3 方和 Gmail 知道的秘密的情况下是无用的

结论

  • 要让攻击者访问您的 Gmail 联系人,它必须侵入您的第 3 方帐户
  • 但是,攻击者永远不会获得访问令牌,因此无法直接对您的 Gmail 联系人执行操作
  • 您可以授权第 3 方访问许多服务,因此您不希望将所有重要令牌都存储在本地计算机上
  • 但是,有一种情况只能使用隐式流:当第 3 方在本地运行,并且没有后端来存储令牌时
  • 那就只能靠前端来存储token了,控制不了

比喻

  • 隐式流程:您向 PROVIDER 索要密钥,您将其存放在您的钱包中,您有责任确保其安全,您直接谨慎使用该密钥,并及时将其更换为新密钥
  • 验证码流程:您要求一个代码,代码被交给您的代客,您的代客将代码和一个秘密文本结合起来,然后与提供者交换密钥,您要求您的代客在需要时使用密钥,但从不自己看到钥匙,您的代客负责更换新钥匙
  • 大多数时候,您的 VALET 比您更注重安全 :)
  • 当您没有代客泊车时,您就得靠自己了

【讨论】:

    【解决方案4】:

    对我们来说,我们的客户希望能够在他们的手机上使用我们的应用进行一次身份验证,而不必一次重新登录数周。使用代码流,您将获得一个刷新令牌以及您的访问令牌。隐式流程不会为您提供刷新令牌。访问令牌的有效期相对较短,但刷新令牌的有效期最长为 90 天。每当访问令牌过期时,客户端和服务器代码都可以使用该刷新令牌来获取新的访问令牌和刷新令牌,所有这些都在后台进行,无需任何用户干预。刷新令牌只能使用一次。你不能用隐式流来做到这一点。如果您使用的是隐式流程,并且您的用户超过一个小时没有与您的应用程序交互,那么当他们回来时,他们将不得不再次登录。这在我们的用例中是不可接受的,而 Code Flow 安全地支持我们的用例。

    这可行且安全,因为可以撤销刷新令牌。如果客户说他们丢失了手机或笔记本电脑,或者黑客进入了他们的桌面,我们可以简单地撤销该用户的所有刷新令牌。在整个过程中,任何个人身份信息 (PII) 都不会触及我们的代码——即用户密码。

    代码流很棒,但确实需要更多的工作。 MS 目前没有 Angular 库来处理它,所以我不得不写一个。如果您有兴趣,我可以帮助您。

    【讨论】:

    • 我认为 MSAL 2.0 现在可以处理了。
    【解决方案5】:

    来自OAuth spec

    4.2。隐式授予

    隐式授权类型用于获取访问令牌(它不 支持刷新令牌的发行)并针对公共进行了优化 已知操作特定重定向 URI 的客户端。这些客户 通常使用脚本语言在浏览器中实现,例如 作为 JavaScript。

    由于这是一个基于重定向的流程,客户端必须能够 与资源所有者的用户代理(通常是网络 浏览器)并能够接收传入的请求(通过重定向) 来自授权服务器。

    与授权码授权类型不同,客户端在授权码授权类型中 单独的授权请求和访问令牌请求, 客户端收到访问令牌作为授权的结果 请求。

    隐式授权类型不包括客户端身份验证,并且 依赖于资源所有者的存在和注册 重定向 URI。因为访问令牌被编码到 重定向URI,它可能会暴露给资源所有者和其他人 驻留在同一设备上的应用程序。

    那么我们可以考虑:

    1. 这适用于公共 OAuth,即当客户端不需要注册并且没有自己的客户端密码时。但是什么身份验证服务器检查重定向 url,这实际上已经足够安全了。

    2. 访问令牌出现在浏览器的地址栏中,因此用户可以复制 url 并将其发送给其他人,它也会以用户身份登录,即类似于会话固定。但是浏览器会通过替换历史记录来进行额外的重定向,以从 url 中删除哈希片段。 黑客也可以通过嗅探 HTTP 流量来窃取访问令牌,但这很容易被 HTTPS 保护。 一些恶意浏览器扩展可以从地址栏访问 url,但这最终是糟糕的情况,比如损坏的 HTTPS 证书。甚至 Auth 代码流在这里也无济于事。 所以我可以看到,通过 url 的哈希片段传递访问令牌是绝对安全的。

    3. 临时访问令牌和刷新令牌的分离在使用 HTTPS 时是没有用的,老实说,即使在原始 HTTP 上也没那么有用。 但是客户端通过隐式流接收不到刷新令牌的事实也是无稽之谈。

    因此我认为我们应该引入一个新的“安全隐式”授权流程,它严格在 https 上工作,允许刷新令牌(或者我们应该完全摆脱它们),并且比 Auth Cose 授权流程更可取

    【讨论】:

      【解决方案6】:

      我的回答是:你不能用 web-app 服务器以安全简单的方式实现隐式流。

      Web-app 授权过程涉及用户交互,因此 Authentication Server 应该在用户身份验证和同意后重定向用户的浏览器返回到 web-app 的目标页面(我没有看到任何其他方式来传递用户在与身份验证服务器进行一些交互后返回 Web 应用程序)。

      所以应该使用重定向 URL 将令牌传递给 web-app,对吧?

      正如@NicolasGarnier 在他的回答和 cmets 中解释的那样,无法将令牌作为 URL 片段传递 - 它不会到达网络应用服务器。

      即使在 HTTPS 下,将令牌作为重定向 URL 的 URL 参数传递也是不安全的:如果目标页面(让它成为“问候页面”)包含资源(图像、脚本等),则该资源将由浏览器获取通过一系列 HTTP(S) 请求(每个请求都有 Referer HTTP 标头,其中包含“问候页面”的确切 URL,包括 URL 参数)。这就是令牌泄漏的方式。

      所以似乎没有办法在重定向 URL 中传递令牌。这就是为什么您需要第二次调用(从身份验证服务器到客户端(但到哪个 URL?)或从客户端到身份验证服务器(授权代码流中的第二次调用))

      【讨论】:

        【解决方案7】:

        在“隐式”流程中,客户端(可能是浏览器)将通过浏览器重定向(GET 操作)获得访问令牌。基于浏览器的通信不安全,您的客户端密码或令牌可能会被拦截或窃取。

        在“授权码”流程中,客户端(通常是 Web 服务器)仅通过浏览器重定向(GET 操作)获得授权码。然后服务器通过对授权服务器进行(非浏览器)POST 调用来将此代码与令牌交换。服务器仅包含用于令牌访问调用的客户端密钥。

        注意 - 根据oauth best practices,“客户端不应使用隐式授权(响应类型“令牌”)或其他响应类型发出访问 授权响应中的令牌”。

        希望这会有所帮助。

        【讨论】:

          【解决方案8】:

          谈到授权码授予类型,我们通过删除对客户端(用户代理或网站)的最终资源的特权访问而获得了更高的安全性,其中客户端(网站所有者)假装是您使用您的授权代码,并避免黑客在您的浏览器上使用 XSS(网站漏洞)进行 CRSF,如果使用隐式方法可能会发生这种情况。

          关键成分是客户端 ID,它出现在向 Auth 服务器发出的第一个请求中。您可以将 Auth Code 步骤视为签名验证。

          此外,即使在完成获取访问令牌的授权代码步骤之后,access_token 最终还是会落到客户端手中。此时,Auth Server 不再需要客户端 ID 来进行签名验证。所以,我不确定授权代码方法是否也完全安全(来自客户端本身)。这就是为什么即使在提供登录凭据之后,身份验证服务器也会要求您同意的原因。这意味着您使用访问令牌信任客户端。

          【讨论】:

            猜你喜欢
            • 2020-11-23
            • 1970-01-01
            • 2016-11-18
            • 2023-04-11
            • 2014-05-10
            • 2020-05-05
            • 2021-12-22
            • 1970-01-01
            • 2018-04-13
            相关资源
            最近更新 更多