当前位置: 首页 > news >正文

【Academy】OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities

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>标签从外部域执行。

相关文章:

【Academy】OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities

OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities 1. 什么是 OAuth&#xff1f;2. OAuth 2.0 是如何工作的&#xff1f;3. OAuth 授权类型3.1 OAuth 范围3.2 授权代码授权类型3.3 隐式授权类型 4. OAuth 身份验证4.1 识别 OAuth 身份验证4.2 侦察OAuth…...

dify中使用NL2SQL

在 Dify 工作流中融入 NL2SQL&#xff08;自然语言转 SQL&#xff09;之能力&#xff0c;可依循如下步骤达成&#xff0c;借由 Dify 的模块化设计以及模型编排之功能&#xff0c;优化数据库查询之智能化交互&#xff1a; 一、环境准备与 Dify 部署 安装 Docker 与 Dify 务须确…...

android viewmodel如何使用

嗯&#xff0c;我现在要学习如何在Android中使用ViewModel。我之前听说过ViewModel是用来管理UI相关数据的&#xff0c;这样在配置变化比如屏幕旋转时数据不会丢失。但具体怎么用呢&#xff1f;我需要先回忆一下相关的知识。 首先&#xff0c;ViewModel应该是Android Architec…...

蓝桥杯备赛-基础训练(四)字符串 day17

好久不见&#xff0c;今天开始继续更新&#xff0c;或许拿不了奖&#xff0c;但是希望记录自己学习的过程&#xff0c;如果感觉有收获的同学在下面多多评论说说我代码的缺陷&#xff0c;感谢大家&#xff01; 1、反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反…...

AI Agent开发框架分析:前端视角

1. Mastra (https://mastra.ai/docs) 优点&#xff1a; 提供直观的界面构建器&#xff0c;适合无代码/低代码开发支持JavaScript/TypeScript&#xff0c;可直接集成到前端项目可视化工作流设计&#xff0c;降低入门门槛内置多种UI组件&#xff0c;加速前端开发 缺点&#xf…...

第3节:IP地址分类与子网划分实战指南

IP地址分类与子网划分实战指南:从小白到入门 在网络通信中,IP地址是设备之间相互识别和通信的基础。无论是家庭网络还是企业网络,IP地址的分配和管理都是网络运维的核心任务之一。然而,对于初学者来说,IP地址的分类、子网掩码、CIDR(无类别域间路由)和VLSM(可变长子网…...

贪心算法三

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是贪心算法&#xff0c;并且掌握贪心算法。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! >…...

pytest基础知识

pytest知识了解 pytest的基础知识了解&#xff1a;Python测试框架之pytest详解_lovedingd的博客-CSDN博客_pytest框架 (包含设置断点&#xff0c;pdb&#xff0c;获取最慢的10个用例的执行耗时) pytest-pytest.main()运行测试用例&#xff0c;pytest参数&#xff1a; pytest-…...

JavaWeb后端基础(7)AOP

AOP是Spring框架的核心之一&#xff0c;那什么是AOP&#xff1f;AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实说白了&#xff0c;面向切面编程就是面向特定方法编程。AOP是一种思想&#xff0c;而在Spring框…...

[AI]从零开始的ComflyUI安装教程

一、前言 AI画图如今已经进化到了让人难以想象的地步。早在2022年各大视频网站上就出现了许多真人使用AI绘制二次元形象的视频。在那个时期&#xff0c;也有人凭借AI画图狠狠的吃到了一波AI红利。在现在&#xff0c;对于普通人来说&#xff0c;AI画图仍然是非常值得探索的。不管…...

文本对抗样本系列的论文阅读笔记(整理合订)

文本对抗样本系列的论文阅读笔记 以前调研文本对抗样本时的论文笔记梳理&#xff0c;论文都很经典&#xff0c;有现成的框架&#xff08;TextAttack&#xff09;可以直接用&#xff0c;论文中部分内容直接是截取自论文&#xff0c;所以存在中英混合笔记的情况。 BERT-Attack …...

鸿基智启:东土科技为具身智能时代构建确定性底座

人类文明的每一次跨越都伴随着工具的革新。从蒸汽机的齿轮到计算机的代码&#xff0c;生产力的进化始终与技术的“具身化”紧密相连。当大语言模型掀起认知革命&#xff0c;具身智能正以“物理实体自主决策”的双重属性重新定义工业、医疗、服务等领域的运行逻辑。在这场革命中…...

javascript-es6 (六)

编程思想 面向过程 面向过程 就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候再一个一个的依次 调用就可以了 就是按照我们分析好了的步骤&#xff0c;按照步骤解决问题 面向对象 面向对象 是把事务分解成为一个个对象&am…...

【leetcode hot 100 19】删除链表的第N个节点

解法一&#xff1a;将ListNode放入ArrayList中&#xff0c;要删除的元素为num list.size()-n。如果num 0则将头节点删除&#xff1b;否则利用num-1个元素的next删除第num个元素。 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...

微信小程序将markdown内容转为pdf并下载

要在微信小程序中将Markdown内容转换为PDF并下载,您可以使用以下方法: 方法一:使用第三方API服务 选择第三方API服务: 可以选择像 Pandoc、Markdown-PDF 或 PDFShift 这样的服务,将Markdown转换为PDF。例如,PDFShift 提供了一个API接口,可以将Markdown内容转换为PDF格式…...

【贪心算法】将数组和减半的最小操作数

1.题目解析 2208. 将数组和减半的最少操作次数 - 力扣&#xff08;LeetCode&#xff09; 2.讲解算法原理 使用当前数组中最大的数将它减半&#xff0c;&#xff0c;直到数组和减小到一半为止&#xff0c;从而快速达到目的 重点是找到最大数&#xff0c;可以采用大根堆快速达到…...

【面试】Kafka

Kafka 1、为什么要使用 kafka2、Kafka 的架构是怎么样的3、什么是 Kafka 的重平衡机制4、Kafka 几种选举过程5、Kafka 高水位了解过吗6、Kafka 如何保证消息不丢失7、Kafka 如何保证消息不重复消费8、Kafka 为什么这么快 1、为什么要使用 kafka 1. 解耦&#xff1a;在一个复杂…...

PHP MySQL 创建数据库

PHP MySQL 创建数据库 引言 在网站开发中&#xff0c;数据库是存储和管理数据的核心部分。PHP 和 MySQL 是最常用的网页开发语言和数据库管理系统之一。本文将详细介绍如何在 PHP 中使用 MySQL 创建数据库&#xff0c;并对其操作进行详细讲解。 前提条件 在开始创建数据库之…...

通义万相 2.1 × 蓝耘智算:AIGC 界的「黄金搭档」如何重塑创作未来?

我的个人主页 我的专栏&#xff1a; 人工智能领域、java-数据结构、Javase、C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01; 点赞&#x1f44d;收藏❤ 引言 在当今数字化浪潮席卷的时代&#xff0c;AIGC&#xff08;生成式人工智能&#xff09;领域正…...

【面试题系列】:使用消息队列怎么防止消息重复?从原理到实战……

一、消息队列的核心价值与挑战 消息队列&#xff08;MQ&#xff09;作为现代分布式系统的基础设施&#xff0c;其核心价值在于解耦、削峰填谷和异步通信。但在追求高可靠性的过程中&#xff0c;消息重复成为必须攻克的技术难题。根据调研数据&#xff0c;在生产环境中消息重复…...

Damage与Injury

### “Damage”和“Injury”的区别 “Damage”和“Injury”都有“损害”或“伤害”的意思&#xff0c;但它们的用法、语境和侧重点有所不同。以下是从词性、适用对象、语义侧重和具体场景四个方面详细对比两者的区别&#xff1a; --- #### 1. **词性** - **Damage**&#xf…...

18 HarmonyOS NEXT UVList组件开发指南(五)

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 第五篇&#xff1a;UVList组件最佳实践与实际应用案例 文章目录 第五篇&#xff1a;UVList组件最佳实践与实际应用案例1. 最佳实践总结1.1 组件设计…...

vue3组合式API怎么获取全局变量globalProperties

设置全局变量 main.ts app.config.globalProperties.$category { index: 0 } 获取全局变量 const { appContext } getCurrentInstance() as ComponentInternalInstance console.log(appContext.config.globalProperties.$category) 或是 const { proxy } getCurrentInstance…...

华为机试牛客刷题之HJ14 字符串排序

HJ14 字符串排序 描述 对于给定的由大小写字母混合构成的 n 个单词&#xff0c;输出按字典序从小到大排序后的结果。 从字符串的第一个字符开始逐个比较&#xff0c;直到找到第一个不同的位置&#xff0c;通过比较这个位置字符对应的&#xff08;A<⋯<Z<a<⋯<…...

CPU 负载 和 CPU利用率 的区别

简单记录下 top 命令中&#xff0c;CPU利用率核CPU负载的概念&#xff0c; &#xff08;1&#xff09;CPU利用率&#xff1a;指在一段时间内 表示 CPU 实际工作时间占总时间的百分比。表示正在执行进程的时间比例&#xff0c;包括用户空间和内核空间程序的执行时间。通常包含以…...

SSM框架

SSM 框架是 Java Web 开发中广泛使用的经典组合&#xff0c;由 Spring、Spring MVC 和 MyBatis 三个开源框架整合而成&#xff0c;适用于构建中大型企业级应用。 1. SSM框架组成 框架作用核心特性Spring管理业务层&#xff08;Service&#xff09;和持久层&#xff08;DAO&am…...

maven无法解析插件 org.apache.maven.plugins:maven-jar-plugin:3.4.1

解决流程 1.修改maven仓库库地址 2.删除本地的maven仓库 maven插件一直加载有问题: 无法解析插件 org.apache.maven.plugins:maven-jar-plugin:3.4.1 开始以为maven版本有问题&#xff0c;重装了maven&#xff0c;重装了idea工具。结果问题还是没解决。研究之后发现&#xf…...

如何修复“RPC 服务器不可用”错误

远程过程调用&#xff08;Remote Procedure Call&#xff0c; RPC&#xff09;是允许客户端在不同计算机上执行进程的众多可用网络进程之一。本文将深入探讨RPC如何在不同的软件系统之间实现无缝消息交换&#xff0c;同时重点介绍与RPC相关的常见错误的一些原因。 什么是远程过…...

晋升系列4:学习方法

每一个成功的人&#xff0c;都是从底层开始打怪&#xff0c;不断的总结经验&#xff0c;一步一步打上来的。在这个过程中需要坚持、总结方法论。 对一件事情长久坚持的人其实比较少&#xff0c;在坚持的人中&#xff0c;不断的总结优化的更少&#xff0c;所以最终达到高级别的…...

单链表-代码精简版

单链表核心知识详解 单链表是一种动态存储的线性数据结构&#xff0c;其特点是逻辑上连续&#xff0c;物理上非连续&#xff0c;每个节点包含数据域和指向下一个节点的指针域。以下是核心知识点与完整实现代码&#xff1a; 一、单链表的结构定义 单链表节点通过结构体自引用…...

关于前后端整合和打包成exe文件的个人的总结和思考

前言 感觉有很多东西&#xff0c;不知道写什么&#xff0c;随便写点吧。 正文 前后端合并 就不说怎么开发的&#xff0c;就说点个人感觉重要的东西。 前端用ReactViteaxios随便写一个demo&#xff0c;用于CRUD。 后端用Django REST Framework。 设置前端打包 import { …...

基于muduo+mysql+jsoncpp的简易HTTPWebServer

一、项目介绍 本项目基于C语言、陈硕老师的muduo网络库、mysql数据库以及jsoncpp&#xff0c;服务器监听两个端口&#xff0c;一个端口用于处理http请求&#xff0c;另一个端口用于处理发送来的json数据。 此项目在实现时&#xff0c;识别出车牌后打包为json数据发送给后端服务…...

Java/Kotlin逆向基础与Smali语法精解

1. 法律警示与道德边界 1.1 司法判例深度剖析 案例一&#xff1a;2021年某游戏外挂团伙刑事案 犯罪手法&#xff1a;逆向《王者荣耀》通信协议&#xff0c;修改战斗数据包 技术细节&#xff1a;Hook libil2cpp.so的SendPacket函数 量刑依据&#xff1a;非法经营罪&#xff…...

C++:入门详解(关于C与C++基本差别)

目录 一.C的第一个程序 二.命名空间&#xff08;namespace&#xff09; 1.命名空间的定义与使用&#xff1a; &#xff08;1&#xff09;命名空间里可以定义变量&#xff0c;函数&#xff0c;结构体等多种类型 &#xff08;2&#xff09;命名空间调用&#xff08;&#xf…...

CI/CD—GitLab钩子触发Jenkins自动构建项目

GitLab钩子简介&#xff1a; 项目钩子 项目钩子是针对单个项目的钩子&#xff0c;会在项目级别的特定事件发生时触发。这些事件包括代码推送、合并请求创建、问题创建等。项目钩子由项目管理员或具有相应权限的用户进行配置&#xff0c;仅对特定项目生效。 使用场景&#xff1a…...

RPA 职业前景:个人职场发展的 “新机遇”

1. RPA职业定义与范畴 1.1 RPA核心概念 机器人流程自动化&#xff08;RPA&#xff09;是一种通过软件机器人模拟人类操作&#xff0c;自动执行重复性、规则性任务的技术。RPA的核心在于其能够高效、准确地处理大量数据和流程&#xff0c;减少人工干预&#xff0c;从而提高工作…...

【CSS3】金丹篇

目录 标准流概念元素类型及排列规则块级元素行内元素行内块元素 标准流的特点打破标准流 浮动基本使用清除浮动额外标签法单伪元素法双伪元素法&#xff08;推荐&#xff09;overflow 法 Flex 布局Flex 组成主轴对齐方式侧轴对齐方式修改主轴方向弹性盒子伸缩比弹性盒子换行行对…...

Git(一)

一、介绍 二、Git代码托管服务 三、Git常用命令 全局设置&#xff1a; 获取Git仓库&#xff1a; 工作区、暂存区、版本库概念&#xff1a; Git工作区文件的状态&#xff1a; 本地仓库操作&#xff1a; 远程仓库操作&#xff1a; 分支操作&#xff1a; 标签操作&#xff1a; 四…...

Python大数据可视化:基于spark的短视频推荐系统的设计与实现_django+spider

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 热门视频界面 用户界面 用户反馈界面 论坛交流界面 系统…...

面试题之react useMemo和uesCallback

在面试中&#xff0c;关于 React 中的 useMemo 和 useCallback 的区别 是一个常见的问题。 useMemo 和 useCallback 的区别 1. 功能定义 useMemo&#xff1a; 用于缓存计算结果&#xff0c;避免在每次组件渲染时重新计算复杂的值。它接受一个计算函数和一个依赖数组&#xff0…...

K8S学习之基础十九:k8s的四层代理Service

K8S四层代理Service 四层负载均衡Service 在k8s中&#xff0c;访问pod可以通过ip端口的方式&#xff0c;但是pod是由生命 周期的&#xff0c;pod在重启的时候ip地址往往会发生变化&#xff0c;访问pod就需要新的ip地址&#xff0c;这样就会很麻烦&#xff0c;每次pod地址改变就…...

C++:string容器(下篇)

1.string浅拷贝的问题 // 为了和标准库区分&#xff0c;此处使用String class String { public :/*String():_str(new char[1]){*_str \0;}*///String(const char* str "\0") // 错误示范//String(const char* str nullptr) // 错误示范String(const char* str …...

sudo systemctl restart docker 重启docker失败

一般会使用如下命令&#xff0c;进行docker重启。 sudo systemctl daemon-reload sudo systemctl restart docker 重启失败时&#xff0c;会提示&#xff1a;Job for docker.service failed because the control process exited with error code. See "systemctl status…...

Linux基本操作指令3

1、wget: 这是一个用于从网络上下载文件的命令行工具。它支持 HTTP、HTTPS 和 FTP 协议。 wget http://download.qt.io/archive/qt/5.12/5.12.9/qt-opensource-linux-x64-5.12.9.run 2、下载完成后&#xff0c;你可以通过以下命令使文件可执行并运行安装程序&#xff1a; ch…...

React:类组件(上)

kerwin老师我来了 类组件的创建 class组件&#xff0c;js里的类命名首字符大写&#xff0c;类里面包括构造函数&#xff0c;方法 组件类要继承React.Component才有效 必须包含render方法 import React from react class App extends React.Component{render() {return <…...

sqli-lab靶场学习(七)——Less23-25(关键字被过滤、二次注入)

前言 之前的每一关&#xff0c;我们都是在末尾加上注释符&#xff0c;屏蔽后面的语句&#xff0c;这样我们只要闭合了区间之后&#xff0c;差不多就是为所欲为的状态。但如果注释符不生效的情况下&#xff0c;又该如何呢&#xff1f; Less23&#xff08;注释符被过滤&#xff…...

虚函数和虚表的原理是什么?

虚函数是一个使用virtual关键字声明的成员函数&#xff0c;在基类中声明虚函数&#xff0c;在子类中可以使用override重写该函数。虚函数根据指针或引用指向的实际对象调用&#xff0c;实现运行时的多态。 虚函数表&#xff08;虚表&#xff09;是一个用于存储虚函数地址的数组…...

RReadWriteLock读写锁应用场景

背景 操作涉及一批数据&#xff0c;如订单&#xff0c;可能存在多个场景下操作&#xff0c;先使用读锁&#xff0c;从redis缓存中获取操作中数据 比如 关闭账单&#xff0c; 发起调账&#xff0c; 线下结算&#xff0c; 合并支付 先判断当前操作的数据&#xff0c;是否在…...

【面试】MySQL

MySQL 1、数据库三范式2、什么是关系型数据库&#xff0c;什么是非关系型数据库3、什么是数据库存储引擎4、MySQL5.x和8.0有什么区别5、char 和 varchar 的区别6、in 和 exists 的区别7、MySQL 时间类型数据存储建议8、drop、delete 与 truncate 区别9、一条 Sql 的执行顺序10、…...

Trae AI 开发工具使用手册

这篇手册将介绍 Trae 的基本功能、安装步骤以及使用方法&#xff0c;帮助开发者快速上手这款工具。 Trae AI 开发工具使用手册 Trae 是字节跳动于 2025 年推出的一款 AI 原生集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在通过智能代码生成、上下文理解和自动化任务…...