您的位置:首页 > 游戏 > 游戏 > 华为云免费服务器_上海网站设计厂家_网站优化软件费用_排行榜百度

华为云免费服务器_上海网站设计厂家_网站优化软件费用_排行榜百度

2025/3/11 8:36:01 来源:https://blog.csdn.net/u013672940/article/details/146158871  浏览:    关键词:华为云免费服务器_上海网站设计厂家_网站优化软件费用_排行榜百度
华为云免费服务器_上海网站设计厂家_网站优化软件费用_排行榜百度

OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities

  • 1. 什么是 OAuth?
  • 2. OAuth 2.0 是如何工作的?
  • 3. OAuth 授权类型
    • 3.1 OAuth 范围
    • 3.2 授权代码授权类型
    • 3.3 隐式授权类型
  • 4. OAuth 身份验证
    • 4.1 识别 OAuth 身份验证
    • 4.2 侦察OAuth 服务
  • 5. OAuth 身份验证漏洞是如何产生的?
  • 6. 利用 OAuth 身份验证漏洞
    • 6.1 OAuth 客户端应用程序中的漏洞
      • 6.1.1 隐式授权类型的实现不当
      • 6.1.2 有缺陷的 CSRF 保护
    • 6.2 OAuth 服务中的漏洞
      • 6.2.1 泄露授权码和访问令牌
        • 6.2.1.1 有缺陷的 redirect_uri 验证
        • 6.2.1.2 通过代理页面窃取代码和访问令牌
      • 6.2.2 有缺陷的范围验证
        • 6.2.2.1 范围升级:授权码流
        • 6.2.2.2 范围升级:隐式流
      • 6.2.3 未验证的用户注册
  • 7. OpenID Connect
    • 7.1 OpenID Connect 的工作原理是什么?
      • 7.1.1 OpenID Connect 角色
      • 7.1.2 OpenID Connect 声明和范围
      • 7.1.3 ID 令牌
    • 7.2 识别 OpenID Connect
    • 7.3 OpenID Connect 漏洞
      • 7.3.1 不受保护的动态客户端注册
      • 7.3.2 允许通过引用进行授权请求
  • 8. 防止 OAuth 身份验证漏洞
    • 8.1 对于OAuth服务提供商
    • 8.2 对于OAuth客户端应用程序


概述
在浏览网页时,您几乎肯定会遇到允许您使用社交媒体帐户登录的网站。此功能很可能是使用流行的 OAuth 2.0 框架构建的。OAuth 2.0 对攻击者来说非常有趣,因为它非常常见,而且本身就容易出现实施错误。这可能会导致许多漏洞,使攻击者能够获取敏感的用户数据并可能完全绕过身份验证。

在本文中将讨论如何识别和利用 OAuth 2.0 身份验证机制中发现的一些关键漏洞。我们还将探讨 OAuth 的 OpenID Connect 扩展中的一些漏洞。最后提供了一些关于如何保护您自己的应用程序免受此类攻击的指南。


1. 什么是 OAuth?

OAuth 是一种常用的授权框架,它使网站和网络应用程序能够请求有限地访问用户在另一个应用程序上的账户。至关重要的是,OAuth 允许用户授予这种访问权限,而无需向请求的应用程序公开其登录凭证。这意味着用户可以微调他们想要共享的数据,而不必将其账户的完全控制权交给第三方。

基本的 OAuth 流程被广泛用于集成第三方功能,这些功能需要访问用户账户中的特定数据。例如,一个应用程序可能使用 OAuth 请求访问你的电子邮件联系人列表,以便它可以推荐与之联系的人。然而,同样的机制也被用于提供第三方身份验证服务,允许用户使用他们在不同网站上拥有的账户登录。

注意
尽管 OAuth 2.0 是当前标准,但一些网站仍然使用旧版 1a。OAuth 2.0 是从头开始编写的,而不是直接从 OAuth 1.0 开发的。因此,两者截然不同。请注意,在这些材料中,术语“OAuth”仅指 OAuth 2.0。


2. OAuth 2.0 是如何工作的?

OAuth 2.0 最初是作为在应用程序之间共享对特定数据的访问权限的一种方式而开发的。它的工作原理是定义三个不同方(即客户端应用程序、资源所有者和 OAuth 服务提供商)之间的一系列交互。

  • 客户端应用程序 - 要访问用户数据的网站或 Web 应用程序。
  • 资源所有者 - 客户端应用程序要访问其数据的用户。
  • OAuth 服务提供商 - 控制用户数据及其访问权限的网站或应用程序。它们通过提供一个用于与授权服务器和资源服务器进行交互的 API 来支持 OAuth。

可以通过多种不同的方式实现实际的 OAuth 过程。这些称为 OAuth“流”或“授权类型”。在本文中,我们将重点介绍“授权代码”和“隐式”授权类型,因为这些是迄今为止最常见的。一般来说,这两种授权类型都涉及以下阶段:

  1. 客户端应用程序请求访问用户数据的一个子集,指定他们想要使用的授权类型以及他们想要的访问类型。
  2. 用户被提示登录到 OAuth 服务,并明确给予对所请求访问的同意。
  3. 客户端应用程序接收一个唯一的访问令牌,该令牌证明他们已获得用户的许可来访问所请求的数据。具体的实现方式因授权类型而异。
  4. 客户端应用程序使用此访问令牌进行 API 调用,从资源服务器获取相关数据。

在了解如何使用 OAuth 进行身份验证之前,了解此基本 OAuth 过程的基础知识非常重要。如果您完全不熟悉 OAuth,我们建议您在进一步阅读之前熟悉我们将要介绍的两种授权类型的详细信息。


3. OAuth 授权类型

在本节中,我们将介绍两种最常见的 OAuth 授权类型的基础知识。


什么是 OAuth 授权类型?
OAuth 授权类型确定 OAuth 过程中涉及的步骤的确切顺序。授权类型还会影响客户端应用程序在每个阶段与 OAuth 服务的通信方式,包括访问令牌本身的发送方式。因此,授权类型通常称为 “OAuth 流”。

OAuth 服务必须配置为支持特定的授权类型,然后客户端应用程序才能启动相应的流程。客户端应用程序在向 OAuth 服务发送的初始授权请求中指定它要使用的授权类型。

有几种不同的授权类型,每种类型都有不同程度的复杂性和安全性注意事项。我们将重点介绍 “authorization code” 和 “implicit” 授权类型,因为这些是迄今为止最常见的。


3.1 OAuth 范围

对于任何 OAuth 授权类型,客户端应用程序都必须指定要访问的数据以及要执行的操作类型。它使用发送到 OAuth 服务的授权请求的 scope 参数来执行此作。

对于基本的 OAuth,客户端应用程序可以请求访问的范围对于每个 OAuth 服务都是唯一的。由于范围的名称只是一个任意的文本字符串,因此其格式在不同的提供商之间可能会有很大差异。有些甚至使用完整的 URI 作为范围名称,类似于 REST API 端点。例如,当请求读取用户的联系人列表的访问权限时,根据所使用的 OAuth 服务,范围名称可能采用以下任何一种形式:

scope=contacts
scope=contacts.read
scope=contact-list-r
scope=https://oauth-authorization-server.com/auth/scopes/user/contacts.readonly

但是,当 OAuth 用于身份验证时,通常会使用标准化的 OpenID Connect 范围。例如,范围 openid profile将授予客户端应用程序对有关用户的一组预定义基本信息(例如其电子邮件地址、用户名等)的读取访问权限。我们稍后将详细讨论 OpenID Connect。


3.2 授权代码授权类型

授权代码授权类型最初看起来相当复杂,但一旦您熟悉了一些基础知识,它实际上比您想象的要简单。

简而言之,客户端应用程序和 OAuth 服务首先使用重定向来交换一系列基于浏览器的 HTTP 请求,这些请求将启动流。系统会询问用户是否同意请求的访问。如果他们接受,则向客户端应用程序授予“授权码”。然后,客户端应用程序将此代码与 OAuth 服务交换以接收“访问令牌”,它们可以使用该令牌进行 API 调用以获取相关的用户数据。

从代码/令牌交换开始发生的所有通信都通过安全的预配置反向通道在服务器到服务器之间发送,因此对最终用户不可见。此安全通道是在客户端应用程序首次向 OAuth 服务注册时建立的。此时,还会生成一个 client_secret,客户端应用程序在发送这些服务器到服务器请求时必须使用该 ID 对自身进行身份验证。

由于最敏感的数据(访问令牌和用户数据)不是通过浏览器发送的,因此这种授权类型可以说是最安全的。理想情况下,服务器端应用程序应尽可能始终使用此授权类型。

Flow for the OAuth authorization code grant type

  1. 授权请求

客户端应用程序向 OAuth 服务的 /authorization 端点发送请求,请求访问特定用户数据的权限。请注意,终端节点映射可能因提供商而异。但是,您应该始终能够根据请求中使用的参数来识别终端节点。

GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com

此请求包含以下值得注意的参数,通常在查询字符串中提供:

  • client_id
    包含客户端应用程序的唯一标识符的必需参数。当客户端应用程序向 OAuth 服务注册时,将生成此值。

  • redirect_uri
    在将授权码发送到客户端应用程序时,用户的浏览器应重定向到的 URI。这也称为 “callback URI” 或 “callback endpoint”。许多 OAuth 攻击都是基于利用此参数验证中的缺陷。

  • response_type
    确定客户端应用程序期望的响应类型,从而确定它要启动的流。对于授权码授权类型,值应为 code

  • scope
    用于指定客户端应用程序要访问的用户数据子集。请注意,这些范围可能是 OAuth 提供者设置的自定义范围,也可能是 OpenID Connect 规范定义的标准化范围。我们稍后将更详细地介绍 OpenID Connect。

  • state
    存储与客户端应用程序上的当前会话相关联的唯一、不可猜测的值。OAuth 服务应在响应中返回此确切值以及授权代码。此参数用作客户端应用程序的 CSRF 令牌形式,确保对其 /callback 端点的请求来自启动 OAuth 流的同一人。

  1. 用户登录并同意

当授权服务器收到初始请求时,它将把用户重定向到登录页面,在那里用户将被提示使用 OAuth 提供方登录他们的账户。例如,这通常是他们的社交媒体账户。

然后,他们将看到客户端应用程序想要访问的数据列表。这基于授权请求中定义的范围。用户可以选择是否同意此访问权限。

需要注意的是,一旦用户为客户端应用程序批准了给定的范围,只要用户与 OAuth 服务仍有有效的会话,此步骤就会自动完成。换句话说,用户第一次选择“使用社交媒体登录”时,他们需要手动登录并给予同意,但如果他们稍后再次访问客户端应用程序,他们通常只需点击一下即可重新登录。

  1. 授权码授予

如果用户同意请求的访问,则其浏览器将被重定向到授权请求的 redirect_uri 参数中指定的 /callback 端点。生成的 GET 请求将包含授权代码作为查询参数。根据配置,它还可能发送与授权请求中值相同的 state 参数。

GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
  1. 访问令牌请求

客户端应用程序收到授权码后,需要将其交换为访问令牌。为此,它会向 OAuth 服务的 /token 端点发送服务器到服务器的 POST 请求。从此以后,所有通信都发生在安全的反向通道中,因此攻击者通常无法观察到或控制。

POST /token HTTP/1.1
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8

除了 client_id 和授权code之外,您还会注意到以下新参数:

  • client_secret
    客户端应用程序必须通过包含注册到 OAuth 服务时为其分配的密钥来验证自身。

  • grant_type
    用于确保新终端节点知道客户端应用程序想要使用哪种授权类型。在这种情况下,应将其设置为 authorization_code

  1. 访问令牌授予

OAuth 服务将验证访问令牌请求。如果一切按预期,服务器将通过向客户端应用程序授予具有请求范围的访问令牌来做出响应。

{"access_token": "z0y9x8w7v6u5","token_type": "Bearer","expires_in": 3600,"scope": "openid profile",…
}
  1. API 调用

现在客户端应用程序有了访问代码,它终于可以从资源服务器获取用户的数据了。为此,它会对 OAuth 服务的 /userinfo 端点进行 API 调用。访问令牌在 Authorization: Bearer 标头中提交,以证明客户端应用程序有权访问此数据。

GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
  1. 资源授权

资源服务器应验证令牌是否有效,以及它是否属于当前客户端应用程序。如果是这样,它将根据访问令牌的范围发送请求的资源(即用户的数据)来做出响应。

{"username":"carlos","email":"carlos@carlos-montoya.net",…
}

客户端应用程序最终可以将此数据用于其预期目的。在 OAuth 身份验证的情况下,它通常将被用作 ID 以授予用户经过身份验证的会话,实际上就是让用户登录。


3.3 隐式授权类型

隐式授权类型要简单得多。客户端应用程序不是先获取授权码,然后将其交换为访问令牌,而是在用户同意后立即接收访问令牌。

您可能想知道为什么客户端应用程序并不总是使用隐式授权类型。答案相对简单 - 它的安全性要低得多。使用隐式授权类型时,所有通信都通过浏览器重定向进行 - 没有像授权代码流那样的安全反向通道。这意味着敏感访问令牌和用户数据更容易受到潜在攻击。

隐式授权类型更适合单页应用程序和本机桌面应用程序,它们无法轻松地将client_secret存储在后端,因此,使用授权代码授权类型不会有太大好处。

Flow for the OAuth implicit grant type

  1. 授权请求

隐式流的启动方式与授权代码流的启动方式大致相同。唯一的主要区别是 response_type 参数必须设置为 token

GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
  1. 用户登录并同意

用户登录并决定是否同意请求的权限。此过程与授权代码流完全相同。

  1. 访问令牌授予

如果用户同意请求的访问权限,这就是事情开始有所不同的地方。OAuth 服务会将用户的浏览器重定向到授权请求中指定的redirect_uri。但是,它不会发送包含授权代码的查询参数,而是将访问令牌和其他特定于令牌的数据作为 URL 片段发送。

GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com

由于访问令牌是在 URL 片段中发送的,因此它永远不会直接发送到客户端应用程序。相反,客户端应用程序必须使用合适的脚本来提取片段并存储它。

  1. API 调用

客户端应用程序从 URL 片段成功提取访问令牌后,即可使用它对 OAuth 服务的 /userinfo 端点进行 API 调用。与授权代码流不同,这也是通过浏览器进行的。

GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
  1. 资源授权

资源服务器应验证令牌是否有效,以及它是否属于当前客户端应用程序。如果是这样,它将通过与访问令牌关联的范围发送请求的资源(即用户的数据)来做出响应。

{"username":"carlos","email":"carlos@carlos-montoya.net"
}

客户端应用程序最终可以将此数据用于其预期目的。在 OAuth 身份验证的情况下,它通常用作 ID 来授予用户经过身份验证的会话,从而有效地登录用户。


4. OAuth 身份验证

虽然 OAuth 最初并非用于此目的,但它也已发展成为一种对用户进行身份验证的方法。例如,您可能熟悉许多网站提供的选项,即使用您现有的社交媒体帐户登录,而不必在相关网站上注册。只要您看到此选项,它很可能是基于 OAuth 2.0 构建的。

对于 OAuth 身份验证机制,基本 OAuth 流基本保持不变;主要区别在于客户端应用程序如何使用它接收的数据。从最终用户的角度来看,OAuth 身份验证的结果大致类似于基于 SAML 的单点登录 (SSO)。在这些材料中,我们将专门关注这个类似 SSO 的使用案例中的漏洞。

OAuth 身份验证通常按如下方式实现:

  1. 用户选择使用其社交媒体帐户登录的选项。然后,客户端应用程序使用社交媒体网站的 OAuth 服务请求访问一些可用于识别用户的数据。例如,这可能是在其账户中注册的电子邮件地址。
  2. 收到访问令牌后,客户端应用程序从资源服务器(通常来自专用的 /userinfo 终端节点)请求此数据。
  3. 收到数据后,客户端应用程序会使用它来代替用户名来登录用户。通常使用从授权服务器接收的访问令牌,而不是传统密码。

只需在通过 Burp 代理流量时完成“使用社交媒体登录”选项,然后研究代理历史记录中的一系列 OAuth 交互。


4.1 识别 OAuth 身份验证

识别应用程序何时使用 OAuth 身份验证相对简单。如果看到有使用不同网站的账户登录的选项,这强烈表明正在使用 OAuth。
识别 OAuth 身份验证最可靠的方法是通过 Burp 代理流量,并在使用此登录选项时检查相应的 HTTP 消息。无论使用哪种 OAuth 授权类型,流程中的第一个请求始终是对/authorization端点的请求,其中包含许多专门用于 OAuth 的查询参数。特别是,留意client_idredirect_uriresponse_type参数。例如,授权请求通常如下所示:

GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com

4.2 侦察OAuth 服务

对正在使用的 OAuth 服务进行一些基本侦察,可以在识别漏洞时为您指明正确的方向。

不言而喻,您应该研究构成 OAuth 流程的各种 HTTP 交互 - 我们将介绍一些具体的事情,以便稍后注意。如果使用外部 OAuth 服务,您应该能够从授权请求发送到的主机名中识别特定提供者。由于这些服务提供了公共 API,因此通常有详细的文档可用,这些文档应该会告诉您各种有用的信息,例如终端节点的确切名称以及正在使用的配置选项。

知道授权服务器的主机名后,应始终尝试向以下标准终结点发送 GET 请求:

  • /.well-known/oauth-authorization-server
  • /.well-known/openid-configuration

这些通常会返回一个 JSON 配置文件,其中包含关键信息,例如可能支持的其他功能的详细信息。这有时会让您了解更广泛的攻击面和支持的功能,而文档中可能未提及这些功能。


5. OAuth 身份验证漏洞是如何产生的?

OAuth 身份验证漏洞的出现部分是因为 OAuth 规范在设计上相对模糊且灵活。尽管每种授权类型的基本功能都需要一些强制性组件,但绝大多数实现都是完全可选的。这包括许多确保用户数据安全所需的配置设置。简而言之,不良做法有很多机会悄悄溜进来。

OAuth 的另一个关键问题是通常缺乏内置的安全功能。安全性几乎完全依赖于开发人员使用正确的配置选项组合,并在其上实施自己的其他安全措施,例如强大的输入验证。正如您可能已经收集到的那样,有很多内容需要吸收,如果您对 OAuth 不熟悉,这很容易出错。

根据授权类型,高度敏感的数据也会通过浏览器发送,这为攻击者提供了各种拦截数据的机会。


6. 利用 OAuth 身份验证漏洞

客户端应用程序的 OAuth 实现以及 OAuth 服务本身的配置中可能会出现漏洞。在本节中,将向您展示如何在这两种情况下利用一些最常见的漏洞。

  • 客户端应用程序中的漏洞
    • 隐式授权类型的实现不当
    • 有缺陷的 CSRF 保护
  • OAuth 服务中的漏洞
    • 泄露授权码和访问令牌
    • 有缺陷的范围验证
    • 未验证的用户注册

6.1 OAuth 客户端应用程序中的漏洞

客户端应用程序通常会使用一个信誉良好、久经考验的 OAuth 服务,该服务针对广为人知的漏洞有良好的防护。然而,它们自身实现的那部分可能安全性较低。

正如我们已经提到的,OAuth 规范的定义相对较为宽松。对于客户端应用程序的实现来说尤其如此。OAuth 流程中有很多动态部分,每种授权类型都有许多可选参数和配置设置,这意味着存在大量错误配置的空间。


6.1.1 隐式授权类型的实现不当

由于通过浏览器发送访问令牌会带来危险,因此主要建议将隐式授权类型用于单页应用程序。但是,由于其相对简单,它也经常用于经典的客户端-服务器 Web 应用程序。

在此流程中,访问令牌作为 URL 片段通过用户的浏览器从 OAuth 服务发送到客户端应用程序。然后,客户端应用程序使用 JavaScript 访问令牌。问题是,如果应用程序想要在用户关闭页面后维护会话,它需要将当前用户数据(通常是用户 ID 和访问令牌)存储在某个位置。

为了解决这个问题,客户端应用程序通常会在POST请求中将此数据提交到服务器,然后为用户分配一个会话 Cookie,实际上是让用户登录。此请求大致等同于作为经典的基于密码的登录的一部分可能发送的表单提交请求。然而,在这种情况下,服务器没有任何秘密或密码可与提交的数据进行比较,这意味着它被隐式信任。

在隐式流中,此POST请求通过攻击者的浏览器暴露给攻击者。因此,如果客户端应用程序未正确检查访问令牌是否与请求中的其他数据匹配,此行为可能会导致严重的漏洞。在这种情况下,攻击者可以简单地更改发送到服务器的参数以冒充任何用户。


6.1.2 有缺陷的 CSRF 保护

虽然 OAuth 流的许多组件是可选的,但其中一些组件强烈建议使用,除非有重要原因不使用它们。一个这样的例子是state参数。
state参数理想情况下应包含一个无法猜测的值,例如在首次启动 OAuth 流时与用户会话相关的某个内容的哈希值。这个值随后在客户端应用程序和 OAuth 服务之间来回传递,作为客户端应用程序的一种 CSRF 令牌形式。因此,如果你注意到授权请求没有发送state参数,从攻击者的角度来看,这是非常有趣的。这可能意味着他们可以在诱使用户的浏览器完成之前自己启动 OAuth 流,类似于传统的 CSRF 攻击。这可能会根据客户端应用程序使用 OAuth 的方式产生严重后果。

考虑一个网站,该网站允许用户使用基于密码的经典机制登录,或者通过使用 OAuth 将其帐户链接到社交媒体配置文件来登录。在这种情况下,如果应用程序无法使用 state 参数,攻击者可能会通过将受害者用户的帐户绑定到他们自己的社交媒体帐户来劫持该帐户。

请注意,如果站点仅允许用户通过 OAuth 登录,那么state参数的重要性可能相对较低。然而,不使用state参数仍然可能使攻击者构建登录 CSRF 攻击,即诱使用户登录到攻击者的账户。


6.2 OAuth 服务中的漏洞


6.2.1 泄露授权码和访问令牌

也许最臭名昭著的基于 OAuth 的漏洞是 OAuth 服务本身的配置使攻击者能够窃取与其他用户帐户关联的授权代码或访问令牌。通过窃取有效的代码或令牌,攻击者可能能够访问受害者的数据。最终,这可能会完全危及他们的帐户 - 攻击者可能会在任何已向此 OAuth 服务注册的客户端应用程序上以受害者用户的身份登录。

根据授权类型,代码或令牌将通过受害者的浏览器发送到授权请求的 redirect_uri 参数中指定的 /callback 端点。如果 OAuth 服务未能正确验证此 URI,攻击者可能能够构建类似 CSRF 的攻击,诱骗受害者的浏览器启动 OAuth 流,将代码或令牌发送到攻击者控制的redirect_uri

在授权码流程的情况下,攻击者可能会在受害者的代码被使用之前窃取它。然后,他们可以将此代码发送到客户端应用程序的合法/callback端点(原始的redirect_uri)以获取对用户帐户的访问权限。在这种情况下,攻击者甚至不需要知道客户端密钥或生成的访问令牌。只要受害者与 OAuth 服务有有效的会话,客户端应用程序就会在代表攻击者完成代码/令牌交换之前将攻击者登录到受害者的帐户。

请注意,使用statenonce保护并不一定能防止这些攻击,因为攻击者可以从他们自己的浏览器生成新的值。

更安全的授权服务器在交换代码时也将要求发送一个redirect_uri参数。然后,服务器可以检查这个参数是否与它在初始授权请求中接收到的参数相匹配,如果不匹配则拒绝交换。由于这是通过安全的反向通道在服务器到服务器的请求中发生的,攻击者无法控制这个第二个redirect_uri参数。


6.2.1.1 有缺陷的 redirect_uri 验证

由于上面涉及的攻击类型,客户端应用程序在注册 OAuth 服务时,最好提供其真实回调 URI 的白名单。这样,当 OAuth 服务收到新请求时,它可以根据此白名单验证 redirect_uri 参数。在这种情况下,提供外部 URI 可能会导致错误。但是,可能仍有方法可以绕过此验证。

在审计 OAuth 流时,您应该尝试使用 redirect_uri 参数来了解它的验证方式。例如:

  • 一些实现通过仅检查字符串是否以正确的字符序列(即批准的域)开头来允许一系列子目录。您应该尝试删除或添加任意路径、查询参数和片段,以查看在不触发错误的情况下可以更改哪些内容。
  • 如果可以将额外值附加到默认 redirect_uri 参数,则可能能够利用 OAuth 服务的不同组件对 URI 的解析之间的差异。例如,您可以尝试以下技术:
https://default-host.com &@foo.evil-user.net#@bar.evil-user.net/

建议您阅读关于如何规避常见 SSRF 防御和 CORS 的内容。

  • 您可能偶尔会遇到服务器端参数污染漏洞。以防万一,您应该尝试提交重复redirect_uri 参数,如下所示:
https://oauth-authorization-server.com/?client_id=123&redirect_uri=client-app.com/callback&redirect_uri=evil-user.net
  • 一些服务器还对 localhost URI 进行了特殊处理,因为它们在开发过程中经常使用。在某些情况下,任何以 localhost 开头的重定向 URI 都可能在生产环境中被意外允许。这可能允许您通过注册域名(如 localhost.evil-user.net)来绕过验证。

需要注意的是,你不应该仅孤立地测试redirect_uri参数。在实际情况中,你通常需要尝试对多个参数进行不同组合的更改。有时,更改一个参数可能会影响其他参数的验证。例如,将response_modequery更改为fragment有时会完全改变redirect_uri的解析,从而允许你提交原本会被阻止的 URI。同样,如果注意到web_message响应模式受支持,这通常会在redirect_uri中允许更广泛的子域。


6.2.1.2 通过代理页面窃取代码和访问令牌

对于更强大的目标,您可能会发现,无论您尝试什么,都无法成功将外部域作为redirect_uri提交。然而,这并不意味着是时候放弃了。

到这个阶段,您应该对 URI 的哪些部分可以篡改有相对更好的了解。现在的关键是利用这些知识来尝试访问客户端应用程序本身中更广泛的攻击面。换句话说,尝试弄清楚是否可以更改 redirect_uri 参数以指向白名单域中的任何其他页面。

尝试找到可以成功访问不同子域或路径的方法。例如,默认 URI 通常位于特定于 OAuth 的路径上,例如 /oauth/callback,该路径不太可能包含任何有趣的子目录。但是,您可以使用目录遍历技巧来提供域上的任何任意路径。像这样:

https://client-app.com/oauth/callback/../../example/path

可以在后端解释为:

https://client-app.com/example/path

确定哪些其他页面可以设置为重定向 URI 后,您应该审核这些页面是否存在可能用于泄露代码或令牌的其他漏洞。对于授权代码流,您需要找到一个允许您访问查询参数的漏洞,而对于隐式授权类型,您需要提取 URL 片段。

为此,最有用的漏洞之一是开放重定向。您可以将其用作代理,将受害者及其代码或令牌转发到攻击者控制的域,在那里您可以托管您喜欢的任何恶意脚本。

请注意,对于隐式授权类型,窃取访问令牌不仅使您能够在客户端应用程序上登录受害者的账户。由于整个隐式流程都是通过浏览器进行的,因此您还可以使用令牌对 OAuth 服务的资源服务器进行自己的 API 调用。这使您能够获取通常无法从客户端应用程序的 Web UI 访问的敏感用户数据。

除了开放重定向之外,您还应该寻找任何其他允许您提取代码或令牌并将其发送到外部域的漏洞。一些很好的例子包括:

  • 处理查询参数和 URL 片段的危险 JavaScript
    例如,不安全的 Web 消息传递脚本在这方面可能非常有用。在某些情况下,你可能需要确定一个更长的小工具链,该链允许你在一系列脚本中传递令牌,然后最终将其泄露到你的外部域。
  • 跨站脚本漏洞
    虽然跨站脚本攻击本身可能会产生巨大影响,但通常攻击者在用户关闭标签页或离开页面之前,能够访问用户会话的时间窗口很小。由于HTTPOnly属性通常用于会话 Cookie,攻击者通常也无法直接使用跨站脚本访问它们。然而,通过窃取 OAuth 代码或令牌,攻击者可以在自己的浏览器中访问用户的帐户。这使他们有更多时间探索用户的数据并执行有害操作,极大地增加了跨站脚本漏洞的严重性。
  • HTML 注入漏洞
    在无法注入 JavaScript 的情况下(例如,由于 CSP 约束或严格筛选),您仍然可以使用简单的 HTML 注入来窃取授权码。如果您可以将 redirect_uri 参数指向可以注入自己的 HTML 内容的页面,则可能会通过 Referer 标头泄露代码。例如,考虑以下 img 元素:<img src="evil-user.net">。尝试获取此图像时,某些浏览器(如 Firefox)将在请求的 Referer 标头中发送完整的 URL,包括查询字符串。

6.2.2 有缺陷的范围验证

在任何 OAuth 流中,用户都必须根据授权请求中定义的范围批准请求的访问权限。生成的令牌允许客户端应用程序仅访问用户批准的范围。但在某些情况下,由于 OAuth 服务的验证存在缺陷,攻击者可能会使用额外权限“升级”访问令牌(无论是被盗的还是使用恶意客户端应用程序获取的)。执行此作的过程取决于授权类型。


6.2.2.1 范围升级:授权码流

借助授权码授权类型,用户的数据通过安全的服务器到服务器通信进行请求和发送,第三方攻击者通常无法直接操纵这种通信。然而,通过向 OAuth 服务注册他们自己的客户端应用程序,仍然有可能实现相同的结果。

例如,假设攻击者的恶意客户端应用程序最初使用openid email范围请求访问用户的电子邮件地址。在用户批准此请求后,恶意客户端应用程序会收到一个授权码。由于攻击者控制着他们的客户端应用程序,他们可以在代码/令牌交换请求中添加另一个scope参数,其中包含额外的profile范围:

POST /token
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8&scope=openid%20 email%20profile

如果服务器没有根据初始授权请求的范围进行验证,它有时会使用新范围生成访问令牌,并将其发送到攻击者的客户端应用程序:

{"access_token": "z0y9x8w7v6u5","token_type": "Bearer","expires_in": 3600,"scope": "openid email profile",…
}

然后,攻击者可以使用他们的应用程序进行必要的 API 调用来访问用户的个人资料数据。


6.2.2.2 范围升级:隐式流

对于隐式授权类型,访问令牌通过浏览器发送,这意味着攻击者可以窃取与无辜客户端应用程序关联的令牌并直接使用它们。一旦他们窃取了访问令牌,他们就可以向 OAuth 服务的 /userinfo 端点发送基于浏览器的普通请求,并在此过程中手动添加新的 scope 参数。

理想情况下,OAuth 服务应根据生成令牌时使用的 scope 值来验证此 scope 值,但情况并非总是如此。只要调整后的权限不超过以前授予此客户端应用程序的访问权限级别,攻击者就有可能访问其他数据,而无需用户的进一步批准。


6.2.3 未验证的用户注册

通过 OAuth 对用户进行身份验证时,客户端应用程序会隐式假设 OAuth 提供程序存储的信息是正确的。这可能是一个危险的假设。

一些提供 OAuth 服务的网站允许用户在不验证所有详细信息(在某些情况下包括他们的电子邮件地址)的情况下注册帐户。攻击者可以利用这一点,通过使用与目标用户相同的详细信息(例如已知的电子邮件地址)在 OAuth 提供程序上注册帐户。然后,客户端应用程序可能会允许攻击者通过此欺诈性的 OAuth 提供程序帐户以受害者的身份登录。


使用 OpenID Connect 扩展 OAuth
当用于身份验证时,OAuth 通常通过 OpenID Connect 层进行扩展,该层提供了一些与识别和身份验证用户相关的附加功能。有关这些功能的详细说明,以及与它们可能引入的漏洞相关的更多信息,请查阅 OpenID Connect 。


7. OpenID Connect

在本节将提供有关 OpenID Connect 的一些关键背景信息,这些信息将帮助您了解我们将要介绍的一些漏洞。


什么是 OpenID Connect?
OpenID Connect 扩展了 OAuth 协议,以提供位于基本 OAuth 实施之上的专用身份和身份验证层。它添加了一些简单的功能,可以更好地支持 OAuth 的身份验证用例。

OAuth 最初在设计时并未考虑身份验证;它旨在作为在应用程序之间委派特定资源的授权的一种方式。但是,许多网站开始自定义 OAuth 以用作身份验证机制。为了实现这一点,他们通常会请求对一些基本用户数据的读取访问权限,如果他们被授予此访问权限,则假定用户在 OAuth 提供者端对自己进行身份验证。

这些普通的 OAuth 身份验证机制远非理想。首先,客户端应用程序无法知道用户何时、何地或如何进行身份验证。由于这些实现中的每一个都是各种自定义解决方法,因此也没有为此目的请求用户数据的标准方法。为了正确支持 OAuth,客户端应用程序必须为每个提供者配置单独的 OAuth 机制,每个机制具有不同的端点、唯一的范围集等。

OpenID Connect 通过添加标准化的、与身份相关的功能来解决许多这些问题,使通过 OAuth 的身份验证以更可靠和统一的方式工作。


7.1 OpenID Connect 的工作原理是什么?

OpenID Connect 巧妙地插入到正常的 OAuth 流中。从客户端应用程序的角度来看,主要区别在于有一组额外的标准化范围,这些范围对所有提供程序都是相同的,还有一个额外的响应类型:id_token


7.1.1 OpenID Connect 角色

OpenID Connect 的角色与标准 OAuth 的角色基本相同。主要区别在于规范使用的术语略有不同。

  • 信赖方 - 请求用户身份验证的应用程序。这与 OAuth 客户端应用程序同义。
  • End user (最终用户) - 正在进行身份验证的用户。这与 OAuth 资源所有者同义。
  • OpenID 提供者 - 配置为支持 OpenID Connect 的 OAuth 服务。

7.1.2 OpenID Connect 声明和范围

术语“claims”是指表示有关资源服务器上用户的信息的key:value对。声明的一个示例可以是 "family_name":"Montoya"

与基本 OAuth 不同,基本 OAuth 的范围对于每个提供者都是唯一的,所有 OpenID Connect 服务都使用一组相同的范围。要使用 OpenID Connect,客户端应用程序必须在授权请求中指定范围 openid。然后,它们可以包含一个或多个其他标准范围:

profile
email
address
phone

这些范围中的每一个都对应于 OpenID 规范中定义的有关用户的声明子集的读取访问权限。例如,请求范围 openid profile将授予客户端应用程序对一系列与用户身份相关的声明的读取访问权限,例如 family_name、given_name、birth_date 等。


7.1.3 ID 令牌

OpenID Connect 提供的另一个主要新增功能是 id_token 响应类型。这将返回使用 JSON Web 签名 (JWS) 签名的 JSON Web 令牌 (JWT)。JWT 有效负载包含基于最初请求的范围的声明列表。它还包含有关 OAuth 服务上次对用户进行身份验证的方式和时间的信息。客户端应用程序可以使用它来确定用户是否已经过充分身份验证。

使用 id_token 的主要好处是减少了需要在客户端应用程序和 OAuth 服务之间发送的请求数,这可以提供更好的整体性能。无需获取访问令牌,然后单独请求用户数据,而是在用户验证自己后立即将包含此数据的 ID 令牌发送到客户端应用程序。

与 Basic OAuth 中发生的情况不同,在 ID 令牌中传输的数据的完整性基于 JWT 加密签名。因此,使用 ID 令牌可能有助于防止某些中间人攻击。但是,鉴于用于签名验证的加密密钥通过同一网络通道传输(通常公开在 /.well-known/jwks.json 上),因此仍可能受到某些攻击。

请注意,OAuth 支持多种响应类型,因此客户端应用程序完全可以同时发送具有基本 OAuth 响应类型和 OpenID Connect id_token响应类型的授权请求:

response_type=id_token token
response_type=id_token code

在这种情况下,ID 令牌和代码或访问令牌将同时发送到客户端应用程序。


7.2 识别 OpenID Connect

如果客户端应用程序正在积极使用 OpenID Connect,则从授权请求中可以明显看出这一点。最万无一失的检查方法是查找强制的 openid 范围。

即使登录过程最初看起来没有使用 OpenID Connect,仍然值得检查 OAuth 服务是否支持它。您可以简单地尝试添加 openid 范围或将响应类型更改为 id_token 并观察这是否会导致错误。

与基本 OAuth 一样,最好查看 OAuth 提供程序的文档,看看是否有任何关于其 OpenID Connect 支持的有用信息。您还可以从标准端点 /.well-known/openid-configuration 访问配置文件。


7.3 OpenID Connect 漏洞

OpenID Connect 的规范比基本 OAuth 的规范严格得多,这意味着通常不太可能出现具有明显漏洞的古怪实现。也就是说,由于它只是位于 OAuth 之上的一个层,因此客户端应用程序或 OAuth 服务可能仍然容易受到我们之前看到的一些基于 OAuth 的攻击。

在本节中,我们将了解 OpenID Connect 的一些额外功能可能引入的一些其他漏洞。


7.3.1 不受保护的动态客户端注册

OpenID 规范概述了允许客户端应用程序向 OpenID 提供商注册的标准化方法。如果支持动态客户端注册,则客户端应用程序可以通过向专用 /registration 端点发送 POST 请求来注册自身。此端点的名称通常在配置文件和文档中提供。

在请求正文中,客户端应用程序以 JSON 格式提交有关自身的关键信息。例如,通常需要包含一组列入白名单的重定向 URI。它还可以提交一系列附加信息,例如要公开的终端节点的名称、应用程序的名称等。典型的注册请求可能如下所示:

POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89{"application_type": "web","redirect_uris": ["https://client-app.com/callback","https://client-app.com/callback2"],"client_name": "My Application","logo_uri": "https://client-app.com/logo.png","token_endpoint_auth_method": "client_secret_basic","jwks_uri": "https://client-app.com/my_public_keys.jwks","userinfo_encrypted_response_alg": "RSA1_5","userinfo_encrypted_response_enc": "A128CBC-HS256",…
}

OpenID 提供程序应要求客户端应用程序对自身进行身份验证。在上面的示例中,他们使用的是 HTTP 持有者令牌。但是,某些提供程序将允许在没有任何身份验证的情况下进行动态客户端注册,这使攻击者能够注册自己的恶意客户端应用程序。这可能会产生各种后果,具体取决于这些攻击者可控制属性的值的使用方式。

例如,您可能已经注意到,其中一些属性可以作为 URI 提供。如果 OpenID 提供商访问了其中任何一个,则除非采取了额外的安全措施,否则这可能会导致二阶 SSRF 漏洞。


7.3.2 允许通过引用进行授权请求

到目前为止,我们已经了解了提交授权请求所需参数的标准方法,即通过查询字符串。一些 OpenID 提供方为你提供了另一种选择,可以将这些参数作为 JSON Web 令牌(JWT)传递。如果支持此功能,你可以发送一个单一的request_uri参数,该参数指向一个包含其余 OAuth 参数及其值的 JSON Web 令牌。根据 OAuth 服务的配置,这个request_uri参数是另一个潜在的服务器端请求伪造(SSRF)载体。

你也可能能够使用此功能绕过这些参数值的验证。一些服务器可能有效地验证授权请求中的查询字符串,但可能无法对 JWT 中的参数(包括redirect_uri)充分应用相同的验证。

要检查是否支持此选项,您应该在配置文件和文档中查找 request_uri_parameter_supported 选项。或者,您可以尝试添加 request_uri 参数以查看它是否有效。您会发现一些服务器支持此功能,即使它们没有在文档中明确提及它。


8. 防止 OAuth 身份验证漏洞

对于开发人员,我们提供了一些指导,告诉您如何避免将这些漏洞引入您自己的网站和应用程序。

为了防止OAuth身份验证漏洞,OAuth提供程序和客户端应用程序都必须对关键输入,尤其是redirect_uri参数实施稳健的验证。OAuth规范中几乎没有内置保护,因此由开发人员自己来确保OAuth流程尽可能安全。

需要注意的是,客户端应用程序和OAuth服务本身都可能出现漏洞。即使您自己的实现坚如磐石,您最终仍然依赖于另一端同样健壮的应用程序。


8.1 对于OAuth服务提供商

  • 要求客户端应用程序注册有效的redirect_uris的白名单。尽可能使用严格的byte-for-byte比较来验证任何传入请求中的URI。只允许完全和精确的匹配,而不是使用模式匹配。这可以防止攻击者访问白名单域上的其他页面。
  • 强制使用state参数。它的值还应该通过包含一些不可猜测的、特定于会话的数据(例如包含会话cookie的哈希)来绑定到用户的会话。这有助于保护用户免受类似CSRF的攻击。它还使攻击者更难使用任何被盗的授权代码。
  • 在资源服务器上,请确保验证访问令牌已发送给发出请求的同一个client_id。您还应该检查请求的范围,以确保它与最初授予令牌的范围相匹配。

8.2 对于OAuth客户端应用程序

  • 在实现OAuth之前,请确保您完全了解OAuth工作原理的细节。许多漏洞是由于简单地缺乏对每个阶段到底发生了什么以及如何利用这些漏洞造成的。
  • 使用state参数,即使它不是强制性的。
  • redirect_uri参数不仅发送到/authorization端点,还发送到/token端点。
  • 在开发移动或本机桌面OAuth客户端应用程序时,通常不可能保持client_secret私有。在这些情况下,可以使用PKCE(RFC 7636)机制来提供额外的保护,防止access code拦截或泄漏。
  • 如果使用OpenID Connect id_token,请确保根据JSON Web签名、JSON Web加密和OpenID规范对其进行了正确验证。
  • 小心使用授权码 - 加载外部图像、脚本或 CSS 内容时,它们可能会通过 Referer 标头泄露。同样重要的是,不要将它们包含在动态生成的 JavaScript 文件中,因为它们可能会通过<script>标签从外部域执行。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com