RFC 9700 OAuth 2.0 安全 BCP 2025 年 1 月
Lodderstedt, et al. 最佳当前实践 [页]
流:
互联网工程任务组 (IETF)
RFC:
9700
BCP:
240
更新:
6749, 6750, 6819
类别:
最佳当前实践
发布:
ISSN:
2070-1721
作者:
T. Lodderstedt
SPRIND
J. Bradley
Yubico
A. Labunets
独立研究员
D. Fett
Authlete

RFC 9700

OAuth 2.0 安全最佳当前实践

摘要

本文档描述 OAuth 2.0 的最佳当前安全实践。它更新 并扩展了 RFC 6749、RFC 6750 和 RFC 6819 中给出的威胁模型和安全建议,以纳入自 OAuth 2.0 发布以来所积累的实践 经验,并涵盖因 OAuth 2.0 更广泛 应用而相关的新威胁。此外,它弃用了某些被认为安全性较低、甚至不安全的 运行模式。

本文档状态

本备忘录记录了一项互联网最佳当前实践。

本文档是互联网工程任务组 (IETF) 的产物。它代表了 IETF 社区的共识。它已经 接受公开审查,并已获 互联网工程指导组 (IESG) 批准发布。有关 BCP 的更多信息 可见 RFC 7841 第 2 节。

有关本文档当前状态、任何 勘误以及如何提供反馈的信息,可从 https://www.rfc-editor.org/info/rfc9700 获取。

目录

1. 简介

[RFC6749][RFC6750] 发布以来,OAuth 2.0(本文档中简称为 “OAuth”)在市场中获得了巨大牵引力, 并已成为 API 保护的标准,以及使用 OpenID Connect [OpenID.Core] 进行联合登录的基础。 虽然 OAuth 被用于 各种场景和不同类型的部署,但可以观察到以下 挑战:

本文档提供更新后的安全建议,以应对这些 挑战。它引入了超出现有 规范(例如 OAuth 2.0 [RFC6749] 和 OpenID Connect [OpenID.Core]) 中定义内容的新要求,并弃用某些被认为安全性较低、甚至 不安全的运行模式。但是,本文档并不取代 [RFC6749][RFC6750][RFC6819] 中给出的安全建议,而是对这些文档进行补充。

自然,并非所有现有生态系统和实现都 与新要求兼容,遵循本文档中描述的最佳实践 可能会破坏互操作性。尽管如此,仍建议 实现者尽快在可行时升级其实现和生态系统。

正在开发中的 OAuth 2.1 [OAUTH-V2.1] 将纳入 本文档中的安全建议。

1.1. 结构

本文档其余部分组织如下:第 2 节 总结了每个 OAuth 实现者最重要的最佳实践。 第 3 节 给出了更新后的 OAuth 攻击者 模型。第 4 节 对 在实际环境中(截至撰写时)可发现的威胁和实现问题 进行详细分析,并讨论潜在对策。

1.2. 约定与 术语

本文档中的关键词 “必须”、“不得”、 “要求”、“”、“不应”、 “应该”、“不应该”、 “建议”、“不建议”、 “可以” 和 “可选” 应按照 BCP 14 [RFC2119] [RFC8174] 中的描述解释,但仅当它们以全大写形式出现时, 如此处所示。

本规范使用 OAuth 2.0 [RFC6749] 定义的术语 “access token”、“authorization endpoint”、“authorization grant”、“authorization server”、“client”、 “client identifier”(client ID)、“protected resource”、“refresh token”、“resource owner”、“resource server” 和 “token endpoint”。

“开放重定向器”是 Web 服务器上的一个端点,它将用户的 浏览器转发到从查询参数获得的任意 URI。

2. 最佳实践

本节描述在撰写时被认为是最佳实践的 核心安全机制和措施集合。关于这些安全机制和措施的细节 (包括详细的攻击 描述)以及较少使用选项的要求,见 第 4 节

2.1. 保护 基于重定向的流程

在将客户端重定向 URI 与预注册 URI 进行比较时, 授权 服务器必须使用精确字符串匹配,但原生应用的 localhost 重定向 URI 中的端口号除外(见 第 4.1.3 节)。此 措施有助于防止授权码和 访问令牌泄露(见 第 4.1 节)。它还可帮助检测 混淆攻击(见 第 4.4 节)。

客户端和授权服务器不得暴露 将用户浏览器转发到 从查询参数获得的任意 URI(开放重定向器)的 URL,如 第 4.11 节 所述。开放 重定向器可能导致 授权码和访问令牌被外泄。

客户端必须防止跨站请求伪造 (CSRF)。在此 上下文中,CSRF 指发送到重定向端点的请求并非 源自授权服务器,而是源自恶意第三方 (详见 [RFC6819] 第 4.4.1.8 节)。已经 确保授权服务器支持 Proof Key for Code Exchange (PKCE) [RFC7636] 的客户端可以 依赖 PKCE 提供的 CSRF 保护。在 OpenID Connect 流程中, nonce 参数提供 CSRF 保护。否则,必须使用 携带在 state 参数中、与用户代理安全绑定的一次性 CSRF 令牌来进行 CSRF 保护(见 第 4.7.1 节)。

当 OAuth 客户端可以与多个授权服务器交互时, 要求具备针对混淆攻击的防御(见 第 4.4 节)。为此,客户端 应该

  • 按照 [RFC9207] 使用 iss 参数作为对策,或者
  • 使用一种基于授权响应中 iss 值的替代对策 (例如 [OpenID.Core] 中 ID Token 的 iss claim,或 [OpenID.JARM] 响应中的该值),并按 [RFC9207] 中的描述处理 该值。

在没有这些选项的情况下,客户端可以 改用不同的重定向 URI 来标识授权端点和令牌端点,如 第 4.4.2 节 所述。

对于重定向可能包含用户 凭据的请求的授权服务器, 必须避免意外转发这些用户凭据(详见 第 4.12 节)。

2.1.1. 授权码 授权

客户端必须防止授权码 注入攻击(见 第 4.5 节),并使用以下选项之一防止授权码被滥用:

  • 为此,公共客户端必须使用 PKCE [RFC7636],其动机见 第 4.5.3.1 节
  • 对于机密客户端, [RFC7636] 建议使用 PKCE,因为如 第 4.5.3.1 节 所述,它能 为防止授权码误用和注入提供强保护。此外,作为副作用, 即使存在 第 4.7.1 节 中所述的强攻击者, 它也能防止 CSRF。
  • 在采取 第 4.5.3.2 节 中描述的额外预防措施后, 机密 OpenID Connect [OpenID.Core] 客户端可以改用 nonce 参数和 ID Token 中相应的 Claim。

无论如何,PKCE challenge 或 OpenID Connect nonce 必须 特定于事务,并与启动该事务的客户端和用户代理 安全绑定。 鼓励授权服务器作出合理努力,检测并 防止对 PKCE challenge 或 OpenID Connect nonce 使用恒定值。

注意:虽然 PKCE 最初被设计为保护原生 应用的机制,但本建议适用于所有类型的 OAuth 客户端,包括 Web 应用。

使用 PKCE 时,客户端应该使用不会在授权请求中 暴露 PKCE verifier 的 PKCE code challenge 方法。 否则,能够读取授权请求的攻击者(参见 攻击者 (A4),见 第 3 节)可以破坏 PKCE 提供的安全性。目前,S256 是唯一这样的方式。

授权服务器必须支持 PKCE [RFC7636]

如果客户端在 授权请求中发送有效的 PKCE code_challenge 参数,授权服务器必须在 令牌端点强制正确使用 code_verifier

授权服务器必须通过确保 仅当授权请求中存在 code_challenge 参数时,才接受包含 code_verifier 参数的 令牌请求,来缓解 PKCE 降级攻击;详见 第 4.8.2 节

授权服务器必须提供一种方式 来检测其对 PKCE 的支持。建议授权服务器在其授权服务器元数据 [RFC8414] 中发布 code_challenge_methods_supported 元素, 其中包含所支持的 PKCE challenge 方法(客户端可用它来检测 PKCE 支持)。授权服务器可以 改为提供一种特定于部署的方式,以确保或确定授权 服务器对 PKCE 的支持。

2.1.2. 隐式授权

隐式授权(response type token)以及其他 导致授权服务器在 授权响应中颁发访问令牌的 response type,容易受到 第 4.14.24.34.6 节所述的访问令牌泄露和 访问令牌重放攻击。

此外,对于在 授权响应中颁发的访问令牌,目前不存在 将访问令牌绑定到特定客户端的标准化发送者约束方法 (如 第 2.2 节 中所建议)。这意味着攻击者可以在资源端点 使用泄露或被盗的访问令牌。

为避免这些问题,客户端不应该 使用隐式 授权(response type token)或其他会在授权响应中颁发 访问令牌的 response type,除非已防止授权响应中的访问令牌注入, 并且已缓解前述令牌泄露 向量。

客户端应该改用 code 这个 response type(即授权码授权类型),如 第 2.1.1 节 所规定,或任何其他 导致授权服务器在令牌 响应中颁发访问令牌的 response type,例如 code id_token response type。这使得 授权服务器能够检测攻击者的重放尝试,并且 通常会减少攻击面,因为访问令牌不会 暴露在 URL 中。它还允许授权服务器对所颁发的令牌 进行发送者约束(见 第 2.2 节)。

2.2. 令牌重放 防护

2.2.1. 访问令牌

发送者约束访问令牌将访问 令牌的适用范围限定到某个发送者。该发送者有义务证明 其知道某个秘密,作为该令牌被 接收方(例如资源服务器)接受的前提。

授权服务器和资源服务器应该 使用对访问令牌进行发送者约束的机制,例如用于 OAuth 2.0 的相互 TLS [RFC8705] 或 OAuth 2.0 Demonstrating Proof of Possession (DPoP) [RFC9449](见 第 4.10.1 节),以防止 被盗和泄露访问令牌的滥用。

2.2.2. 刷新令牌

公共客户端的刷新令牌必须 进行发送者约束,或使用 第 4.14 节 中所述的刷新 令牌轮换。[RFC6749] 已经 要求机密客户端的刷新令牌只能由其被颁发给的 客户端使用。

2.3. 访问令牌权限 限制

与访问令牌关联的权限应该限制为 特定应用或用例所需的最低权限。这 防止客户端超出 资源所有者授权的权限。它还防止用户超出其 相应安全策略所授权的权限。权限限制 还有助于降低访问令牌泄露的影响。

特别是,访问令牌应该 被受众限制到特定资源 服务器,或者在不可行时限制到一小组资源服务器。为实现这一点, 授权服务器将 访问令牌与某些资源服务器关联,并且每个资源 服务器都有义务针对每个请求验证,该请求中发送的访问 令牌是否意在用于该特定 资源服务器。如果不是,资源服务器必须拒绝 服务相应请求。[RFC9068] 中定义的 aud claim 可以 用于对访问令牌进行受众限制。客户端和授权服务器可以利用 [RFC6749][RFC8707] 分别规定的参数 scoperesource,来确定它们想要访问的 资源服务器。

此外,访问令牌应该限制到 资源服务器或资源上的某些资源 和动作。为实现这一点, 授权服务器将访问令牌与 相应资源和动作关联,并且每个资源服务器都有义务 针对每个请求验证,该请求中发送的访问令牌是否 意在用于该特定资源上的 特定动作。如果不是,资源服务器必须拒绝服务 相应请求。客户端和授权服务器可以利用 [RFC6749] 中规定的参数 scope,以及 [RFC9396] 中规定的 authorization_details, 来确定这些资源和/或动作。

2.4. 资源所有者 密码凭据授权

资源所有者密码凭据授权 [RFC6749] 不得 使用。此授权类型会以不安全的方式将资源 所有者的凭据暴露给客户端。即使客户端是良性的,使用此授权也会导致 攻击面增加(即凭据可能在授权服务器之外的更多地方泄露), 并训练用户在授权 服务器之外的位置输入其凭据。

此外,资源所有者密码凭据授权并非设计用于 配合双因素身份认证以及需要 多个用户交互步骤的身份认证流程。使用密码学凭据进行身份认证 (参见 WebCrypto [W3C.WebCrypto]、 WebAuthn [W3C.WebAuthn])可能 无法用这种授权类型实现,因为它通常绑定到特定的 Web origin。

2.5. 客户端身份认证

如果在特定部署中可行,可以建立 客户端凭据颁发/注册流程,并确保这些凭据的机密性, 则授权服务器应该强制执行客户端 身份认证。

建议使用非对称密码学进行 客户端身份认证,例如用于 OAuth 2.0 的相互 TLS [RFC8705],或符合 [RFC7521][RFC7523] 的签名 JWT (“Private Key JWT”)。后者在 [OpenID.Core] 中定义为客户端 身份认证方法 private_key_jwt)。 当使用非对称密码学进行客户端身份认证时,授权 服务器无需存储敏感的对称密钥,使这些 方法对密钥泄露更具鲁棒性。

2.6. 其他建议

使用 OAuth 授权服务器元数据 [RFC8414] 可以帮助提升 OAuth 部署的安全性:

  • 它确保合规的软件库能够自动启用 安全功能和其他新的 OAuth 功能。
  • 它降低配置错误的可能性—— 例如,配置错误的端点 URL(可能属于攻击者)或配置错误的安全功能。
  • 它可以帮助促进密码学 密钥轮换,并确保 密码学敏捷性。

因此,建议授权 服务器按照 [RFC8414] 发布 OAuth 授权服务器元数据, 并建议客户端使用该授权服务器元数据(可用时)来配置自身。

第 4.15.1 节 所述条件下, 授权服务器不应该允许客户端影响其 client_id 或 任何其他可能导致与真实资源所有者混淆的 claim。

建议在客户端与 资源服务器之间按照 [BCP195] 使用端到端 TLS。如果 TLS 流量需要在中间方终止,请参阅 第 4.13 节 以获取进一步的安全 建议。

授权响应不得通过未加密的网络 连接传输。为此,授权服务器不得允许 使用 http scheme 的重定向 URI,但使用回环接口重定向的原生客户端除外, 如 [RFC8252] 第 7.3 节 所述。

如果授权响应通过类似 postMessage [WHATWG.postmessage_api] 的浏览器内通信技术 发送,而不是通过 HTTP 重定向发送,则浏览器内消息的发起方和接收方 必须第 4.17 节 所述进行严格 验证。

为支持基于浏览器的客户端,此类客户端直接访问的端点 (包括 Token Endpoint、Authorization Server Metadata Endpoint、jwks_uri Endpoint 和 Dynamic Client Registration Endpoint)可以支持 使用 Cross-Origin Resource Sharing (CORS) [WHATWG.CORS]。 但是,授权端点不得 支持 CORS,因为客户端不会直接访问此 端点;相反,客户端会将用户代理重定向到该端点。

3. 更新后的 OAuth 2.0 攻击者模型

[RFC6819] 中,提出了一个 威胁模型,用于描述 OAuth 部署必须防护的威胁。在此过程中,[RFC6819] 对攻击者及其能力作出了 某些假设,也就是说,它隐含地 建立了一个攻击者模型。下面将显式说明该攻击者模型, 并对其进行更新和扩展,以考虑涉及多个参与方的潜在动态 关系(如 第 1 节 所述),纳入 新类型的攻击者,并更清晰地定义攻击者模型。

本文档的目标是确保资源 所有者(通过用户代理)在授权服务器上的授权,以及随后在 资源服务器上使用访问令牌的过程,在实际可行范围内 至少能抵御以下攻击者。

(A1)

Web 攻击者可以建立并运行任意数量的 网络端点(除“诚实”的端点之外),包括浏览器和 服务器。Web 攻击者可以建立资源所有者会访问的网站, 运行自己的用户代理,并参与协议。

特别是,Web 攻击者可以运行在 授权服务器上注册的 OAuth 客户端,并且可以运行自己的授权服务器和 资源服务器,资源所有者和其他资源所有者可以(与“诚实”的服务器并行)使用这些服务器。

还必须假定 Web 攻击者可以随时诱使用户 将其浏览器导航到攻击者任意选择的 URI。实践中,这 可以通过多种方式实现,例如,向广告网络中注入恶意 广告,或发送 看似合法的电子邮件。

Web 攻击者可以使用自己的用户凭据来创建新 消息,也可以使用其先前获知的任何秘密。例如, 如果 Web 攻击者通过配置错误的重定向 URI 获知了用户的授权码, 则该 Web 攻击者随后可以 尝试将该授权码兑换为访问令牌。

但是,他们不能读取或操纵并非 发送给他们的消息(例如,发送到不受攻击者控制的授权服务器 URL 的消息)。

(A2)

网络攻击者还对协议参与者通信所经由的 网络拥有完全控制权。他们可以 窃听、操纵和伪造消息,但当这些 消息受到密码学方法(例如 TLS)正确保护时除外。 网络攻击者还可以阻止任意消息。

Web 攻击者的一个例子可以是互联网 服务提供商的客户,而网络攻击者可以是该互联网服务 提供商本身、在公共(Wi-Fi)网络中使用 ARP 欺骗的攻击者,或例如能够访问互联网 交换点的国家支持攻击者。

上述攻击者 (A1)(A2) 符合 OAuth 形式化分析 工作中使用的攻击者模型 [arXiv.1601.01229]。 这是一个最小攻击者模型。 实现者必须考虑其 OAuth 实现环境中的所有可能类型的攻击者。 例如,在 [arXiv.1901.11520] 中, 使用了一个非常强的攻击者模型,其中包括对 令牌端点拥有完全控制权的攻击者。这建模了 生态系统中端点可能配置错误所造成的影响,而这种情况可以通过使用 第 2.6 节 中所述的授权服务器元数据来避免。因此,此类攻击者未在此列出。

然而,以往针对 OAuth 的攻击表明,以下类型的 攻击者尤为相关:

(A3)

能够读取但不能修改 授权响应内容的攻击者(即授权响应可能 泄露给攻击者)。

此类攻击的例子包括开放重定向器攻击和 混淆攻击(见 第 4.4 节),其中客户端被诱骗 将凭据发送到攻击者控制的授权 服务器。

此外,这还包括利用以下因素的攻击:

  • 对重定向 URI 检查不足(见 第 4.1 节);
  • 移动操作系统中存在的问题,其中不同 应用可以将自身注册到同一 URI;以及
  • 浏览器(历史记录)、代理 服务器和操作系统存储/记录的 URL。
(A4)

能够读取但不能修改 授权请求内容的攻击者(即授权请求可能 以上述相同方式泄露给攻击者)。

(A5)

能够获取由授权服务器颁发的访问令牌的攻击者。 例如,资源服务器可能被攻击者攻陷, 访问令牌可能因配置错误而被发送到攻击者控制的资源服务器, 或者可能使用社会工程诱使资源所有者 使用攻击者控制的资源服务器。另见 第 4.9.2 节

(A3)(A4)(A5) 通常会与 (A1)(A2) 同时出现。 攻击者可以协作以达成共同目标。

注意,攻击者 (A1)(A2) 可以是资源所有者,或 扮演资源所有者。例如,此类攻击者可以使用自己的浏览器, 在客户端或资源服务器处重放通过上述任何攻击获得的 令牌或授权码。

本文档重点关注由攻击者 (A1)(A5) 造成的威胁。

4. 攻击与缓解措施

本节详细描述针对 OAuth 实现的攻击, 并给出潜在对策。已在 [RFC6819] 中涵盖的攻击和缓解措施不会在此列出,除非提出了新的 建议。

本节还为某些情形和协议 选项定义了额外要求(超出 第 2 节 中定义的要求)。

4.1. 重定向 URI 验证不足

某些授权服务器允许客户端注册重定向 URI 模式,而不是完整的重定向 URI。随后授权服务器 在运行时将授权 端点上的重定向 URI 参数值与已注册的模式匹配。这种方法 允许客户端将事务状态编码到额外的重定向 URI 参数中,或为多个 重定向 URI 注册单个模式。

事实证明,与精确重定向 URI 匹配相比,这种方法实现起来更复杂, 管理起来也更容易出错。实践中已经观察到多起 成功攻击,它们利用了模式匹配 实现或具体配置中的缺陷(例如,见 [research.rub2])。 对重定向 URI 验证不足会有效破坏 客户端识别或身份认证(取决于授权和客户端 类型),并允许攻击者通过以下方式获得授权码或 访问令牌:

  • 直接将用户代理发送到攻击者 控制下的 URI,或
  • 利用客户端上的开放重定向器以及用户 代理处理 URL 片段的方式, 将 OAuth 凭据暴露给攻击者。

这些攻击在以下小节中详细展示。

4.1.1. 针对授权码授权的重定向 URI 验证攻击

对于使用授权类型 code 的客户端,攻击可能 如下进行:

假设重定向 URL 模式 https://*.somesite.example/* 已 为客户端 ID 为 s6BhdRkqt3 的客户端注册。其 意图是允许 somesite.example 的任意子域成为该客户端的 有效重定向 URI,例如 https://app1.somesite.example/redirect。然而,授权服务器上的朴素实现可能会将 通配符 * 解释为 “任意字符”,而不是“域名中有效的任意字符”。因此, 授权服务器可能允许 https://attacker.example/.somesite.example 作为重定向 URI, 尽管 attacker.example 是一个不同的域,并可能 由恶意方控制。

随后可按如下方式实施攻击:

首先,攻击者需要诱骗用户在其浏览器中打开一个 被篡改的 URL,该 URL 会启动一个由攻击者 控制的页面,例如 https://www.evil.example(见 第 3 节 中的攻击者 A1)。

此 URL 使用合法客户端的客户端 ID 向授权端点发起以下授权请求(换行 仅用于显示):

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13
     &redirect_uri=https%3A%2F%2Fattacker.example%2F.somesite.example
     HTTP/1.1
Host: server.somesite.example

授权服务器验证重定向 URI,并将其与 客户端 s6BhdRkqt3 的已注册重定向 URL 模式进行比较。 授权请求被处理并呈现给用户。

如果用户没有看到重定向 URI,或没有识别出 该攻击,则会颁发授权码,并立即将其发送到攻击者的 域。如果启用了授权的自动批准 (根据 [RFC6749],不建议公共客户端这样做),则攻击甚至可以在没有用户 交互的情况下执行。

如果攻击者冒充公共客户端,则攻击者可以 在相应的令牌端点将授权码兑换为令牌。

对于机密客户端,此攻击不容易奏效,因为 授权码交换需要使用合法客户端的 秘密进行身份认证。但是,攻击者可以使用合法机密 客户端通过执行授权码 注入攻击来兑换授权码;见 第 4.5 节

需要注意的是,即使授权 服务器正确处理通配符,仍可能存在重定向 URI 验证 漏洞。例如,假设客户端 注册重定向 URL 模式 https://*.somesite.example/*,且 授权服务器将其解释为“允许指向 somesite.example 域中任意主机的重定向 URI”。如果攻击者设法在 somesite.example 中建立一个主机或子域,则攻击者可以冒充合法客户端。 例如,这 可能由子域接管攻击造成 [research.udel],其中一个 过时的 CNAME 记录(例如 external-service.somesite.example) 指向一个不再存在的外部 DNS 名称(例如 customer-abc.service.example),并可被攻击者接管 (例如,通过在外部服务上注册为 customer-abc)。

4.1.2. 针对隐式授权的重定向 URI 验证攻击

上述攻击同样适用于隐式授权。如果 攻击者能够将授权响应发送到攻击者控制的 URI, 攻击者将直接访问携带 访问令牌的片段。

此外,隐式授权(以及使用 [OAuth.Responses] 中定义的 response_mode=fragment 时的其他授权) 也可能遭受另一类 攻击。该攻击利用了这样一个事实:如果 Location 标头不 包含片段,用户代理会将片段重新附加到重定向的目标 URL 上(见 [RFC9110] 第 17.11 节)。这里描述的攻击 将这种行为与作为开放 重定向器的客户端(见 第 4.11.1 节)结合起来,以获得访问令牌。这使得 即使非常狭窄的重定向 URI 模式也可能被绕过,但严格 URL 匹配不会被绕过。

假设客户端 s6BhdRkqt3 的已注册 URL 模式 是 https://client.somesite.example/cb?*,即允许向 https://client.somesite.example/cb 的重定向 携带任意参数。不幸的是, 该客户端暴露了一个开放重定向器。该端点支持一个 参数 redirect_to,它接受目标 URL,并会使用 HTTP Location 标头重定向 303 将 浏览器发送到该 URL。

现在可以按如下方式实施攻击:

首先,与上面一样,攻击者需要诱骗用户在其浏览器中打开 一个被篡改的 URL,该 URL 会启动一个由 攻击者控制的页面,例如 https://www.evil.example

随后,该网站发起一个授权请求,它与针对 code flow 的攻击中的请求非常相似。 与上面不同的是,它通过将 redirect_to=https://attacker.example 编码进 重定向 URI 的参数来利用开放重定向器,并且使用 response type token (换行仅用于显示):

GET /authorize?response_type=token&state=9ad67f13
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.somesite.example
     %2Fcb%26redirect_to%253Dhttps%253A%252F
     %252Fattacker.example%252F HTTP/1.1
Host: server.somesite.example

然后,由于重定向 URI 匹配已注册模式, 授权服务器允许该请求,并在 303 重定向中发送生成的访问 令牌(为便于阅读,省略了一些响应参数):

HTTP/1.1 303 See Other
Location: https://client.somesite.example/cb?
          redirect_to%3Dhttps%3A%2F%2Fattacker.example%2Fcb
          #access_token=2YotnFZFEjr1zCsicMWpAA&...

在 client.somesite.example 上,请求到达开放 重定向器。该端点将 读取 redirect 参数,并向 URL https://attacker.example/ 发出 HTTP 303 Location 标头 重定向。

HTTP/1.1 303 See Other
Location: https://attacker.example/

由于 client.somesite.example 上的重定向器没有在 Location 标头中包含 片段,用户代理会将原始 片段 #access_token=2YotnFZFEjr1zCsicMWpAA&... 重新附加到 URL,并导航到以下 URL:

https://attacker.example/#access_token=2YotnFZFEjr1z...

随后,位于 attacker.example 的攻击者页面可以 访问该 片段并获得访问令牌。

4.1.3. 对策

正确实现和管理模式匹配的复杂性显然 会造成安全问题。因此,本文档建议通过使用精确重定向 URI 匹配来简化所需的 逻辑和配置。这意味着 授权服务器必须确保两个 URI 相等; 详见 [RFC3986] 第 6.2.1 节,简单字符串比较。 唯一例外是 使用 localhost URI 的原生应用:在这种情况下,授权服务器 必须允许可变 端口号,如 [RFC8252] 第 7.3 节 所述。

其他建议:

  • 托管重定向 URI 的 Web 服务器 不得暴露开放 重定向器(见 第 4.11 节)。
  • 浏览器只有在 Location 标头中的 URL 尚未包含片段时,才会将 URL 片段重新附加到 Location 重定向 URL。 因此,服务器可以通过在 Location 标头中的 URL 后附加任意片段标识符(例如 #_),来阻止浏览器将片段 重新附加到重定向 URL。
  • 客户端应该使用 授权码 response type,而不是会在授权 端点导致访问令牌颁发的 response type。这通过与授权 服务器的交换过程,为泄露凭据的重用提供对策, 并通过对访问令牌进行发送者约束来防止令牌 重放。

如果可以验证包含 重定向 URI 的授权请求的来源和完整性,例如,在使用 [RFC9101][RFC9126] 并配合客户端 身份认证时,则授权服务器可以信任该 重定向 URI,而无需进一步检查。

4.2. 通过 Referer 标头泄露凭据

授权请求 URI 或授权 响应 URI 的内容,可能会通过 Referer HTTP 标头(见 [RFC9110] 第 10.1.3 节)无意中披露给攻击者, 分别来自授权服务器或客户端的网站泄露。最 重要的是,授权码或 state 值可能以 这种方式被披露。尽管 [RFC9110] 第 10.1.3 节 中另有规定, 由于浏览器实现问题,通过 URI 片段传递的访问令牌也可能发生相同情况, Chromium 项目中一个(现已修复的)问题对此作了说明 [bug.chromium]

4.2.1. 来自 OAuth 客户端的泄露

来自 OAuth 客户端的泄露要求客户端在成功授权请求的结果下 渲染一个页面,该页面

  • 包含指向攻击者控制下其他页面的链接,并且 用户点击了这样的链接,或
  • 包含第三方内容(iframe 中的广告、图片 等),例如,如果页面包含用户生成内容 (博客)。

一旦浏览器导航到攻击者页面,或加载第三方内容, 攻击者就会收到授权响应 URL,并能够提取 codestate(以及可能的 access_token)。

4.2.2. 来自 授权服务器的泄露

类似地,如果授权服务器上的授权端点 包含上述链接或第三方内容,攻击者可以从 授权请求中获知 state

4.2.3. 后果

通过 Referer 标头获知有效授权码或访问令牌的攻击者, 可以执行第 4.1.14.54.6 节所述的攻击。如果攻击者 获知 state,则使用 state 实现的 CSRF 保护会失效,从而导致 [RFC6819] 第 4.4.1.8 节 所述的 CSRF 攻击。

4.2.4. 对策

作为 OAuth 授权响应结果而渲染的页面,以及 授权端点不应该包含第三方 资源或指向外部站点的 链接。

以下措施可进一步降低攻击成功的 可能性:

  • 通过向文档应用适当的 Referrer Policy [W3C.webappsec-referrer-policy] 来抑制 Referer 标头 (作为 "referrer" meta 属性的一部分,或通过设置 Referrer-Policy 标头)。例如,响应中的标头 Referrer-Policy: no-referrer 会完全抑制从生成文档发起的所有请求中的 Referer 标头。
  • 使用授权码,而不是会从授权端点 颁发访问 令牌的 response type。
  • 将授权码绑定到机密 客户端或 PKCE challenge。在这种情况下,攻击者缺少请求 授权码交换所需的秘密。
  • [RFC6749] 第 4.1.2 节 所述,授权码 必须在令牌端点首次使用后由授权服务器使其失效。 例如,如果授权服务器在合法客户端兑换授权码后使其失效, 则攻击者稍后兑换 该授权码将失败。

    如果攻击者设法在合法客户端之前 将授权码兑换为令牌,则这不能缓解攻击。因此, [RFC6749] 进一步建议,当有人尝试两次兑换同一授权码时,授权服务器应该撤销此前基于该授权码颁发的所有 令牌。

  • state应该在客户端于重定向端点首次使用后使其失效。 如果实现了这一点,并且 攻击者通过来自 客户端网站的 Referer 标头收到令牌,则 state 已被使用并由 客户端使其失效,攻击者无法再次使用它。(如果 state 从 授权服务器网站泄露,这并无帮助,因为此时 state 尚未在客户端的重定向端点使用。)

  • 对授权响应使用 form post response mode, 而不是重定向 (见 [OAuth.Post])。

4.3. 通过 浏览器历史记录泄露凭据

授权码和访问令牌可能最终出现在浏览器的 已访问 URL 历史记录中,从而引发 以下所述的攻击。

4.3.1. 浏览器历史记录中的授权 码

当浏览器因提供者授权端点的 重定向而导航到 client.example/redirection_endpoint?code=abcd 时,包含 授权码的 URL 可能最终进入浏览器历史记录。能够访问该设备的 攻击者可获得授权码并尝试 重放它。

对策:

  • [RFC6819] 第 4.4.1.1 节 以及 第 4.5 节 所述的授权码重放防护。
  • 对授权响应使用 form post response mode, 而不是重定向 (见 [OAuth.Post])。

4.3.2. 浏览器历史记录中的访问令牌

如果客户端或已经拥有令牌的网站故意导航到类似 provider.com/get_user_profile?access_token=abcdef 的页面,访问令牌可能最终进入浏览器历史记录。 [RFC6750] 不鼓励这种做法,并建议通过标头传输令牌, 但实践中,网站经常在查询 参数中传递访问令牌。

在隐式授权的情况下,类似 client.example/redirection_endpoint#access_token=abcdef 的 URL 也可能因提供者授权端点的重定向而最终进入 浏览器历史记录。

对策:

  • 客户端不得[RFC6750] 第 2.3 节 所述方式在 URI 查询参数中传递访问令牌。 为此,可以使用授权 码授权或替代 OAuth response mode,例如 form post response mode [OAuth.Post]

4.4. 混淆攻击

混淆攻击可能发生在 OAuth 客户端与 两个或多个授权服务器交互,且至少一个授权 服务器受攻击者控制的场景中。例如,如果攻击者使用动态注册将 客户端注册到自己的授权服务器,或者如果某个授权服务器 被攻陷,就可能出现这种情况。

攻击的目标是获得未被攻陷的授权服务器的授权码或访问 令牌。实现方式是 诱骗客户端将这些凭据发送到被攻陷的 授权服务器(攻击者),而不是在 未被攻陷的授权/资源 服务器的相应端点上使用它们。

4.4.1. 攻击描述

这里的描述遵循 [arXiv.1601.01229],并在下面概述攻击的 变体。

前置条件:为使此攻击变体生效,假定

  • 隐式授权或授权码授权与多个授权服务器 一起使用,其中一个被认为是“诚实的”(H-AS),另一个由 攻击者运行 (A-AS),并且
  • 客户端将用户选择的授权服务器存储在绑定到 用户浏览器的会话中,并对 每个授权服务器使用相同的重定向 URI。

下面进一步假设客户端已在 H-AS(URI: https://honest.as.example,客户端 ID: 7ZGZldHQ)以及 A-AS (URI: https://attacker.example,客户端 ID: 666RVZJTA)注册。以下示例中的 URL 为便于展示而缩短,仅包含与 攻击相关的参数。

针对授权码授权的攻击:

  1. 用户选择使用 A-AS 启动授权 (例如,通过点击客户端网站上的按钮)。
  2. 客户端在用户会话中存储用户选择了 “A-AS”,并将用户重定向到 A-AS 的授权端点, 其中 Location 标头包含 URL https://attacker.example/authorize?response_type=code&client_id=666RVZJTA
  3. 当用户浏览器导航到攻击者的 授权端点时, 攻击者立即将浏览器重定向到 H-AS 的授权端点。在授权请求中, 攻击者将 A-AS 处客户端的客户端 ID 替换为 H-AS 处的客户端 ID。 因此,浏览器 收到一个重定向(303 See Other),其中 Location 标头指向 https://honest.as.example/authorize?response_type=code&client_id=7ZGZldHQ
  4. 用户授权客户端访问其在 H-AS 的 资源。(注意,警觉的用户此时可能会发现他们本来打算使用 A-AS 而不是 H-AS。列出的第一个攻击变体没有此 限制。)H-AS 颁发授权码,并将其(通过浏览器)发送回客户端。

  5. 由于客户端仍假定该授权码由 A-AS 颁发, 因此它会尝试在 A-AS 的令牌端点兑换该授权码。

  6. 因此,攻击者获得授权码,并可将 该授权码兑换为访问令牌(对于公共客户端),或执行 授权码注入攻击,如 第 4.5 节 所述。

变体:

  • 带拦截的混淆:仅当攻击者能够 拦截并操纵从用户 浏览器到客户端的第一对请求/响应(其中用户选择某个授权服务器,并随后 由客户端重定向到该授权服务器)时,此变体才有效,如 攻击者 (A2)(见 第 3 节)所示。该能力 例如可能来自对用户到客户端连接的中间人攻击。 在攻击中,用户使用 H-AS 启动流程。 攻击者拦截此请求,并将用户的选择改为 A-AS。攻击的其余部分按上面 步骤 2 及其后的内容继续。
  • 隐式授权:在隐式授权中, 攻击者在 步骤 4 中收到访问 令牌而不是授权码。当客户端向 A-AS userinfo 端点(定义于 [OpenID.Core])发出请求,或向攻击者的资源服务器发出请求时(因为客户端认为自己已 完成与 A-AS 的流程),攻击者的授权服务器会收到访问令牌。
  • 每个 AS 使用不同的重定向 URI:如果客户端为 不同授权服务器使用不同的重定向 URI,客户端不在用户会话中存储所选授权 服务器,并且授权服务器 未正确检查重定向 URI,则攻击者可以发起一种称为 “跨社交网络请求伪造”的攻击。这些攻击已在实践中 观察到。详见 [research.jcs_14]
  • OpenID Connect:某些变体可用于攻击 OpenID Connect。在这些攻击中,攻击者滥用 OpenID Connect Discovery [OpenID.Discovery] 机制的功能,或重放访问 令牌或 ID Tokens 来进行混淆攻击。这些攻击在 [arXiv.1704.08539] 的附录 A 和 [arXiv.1508.04324v2] 的第 6 节 (“恶意端点攻击”)中有详细描述。

4.4.2. 对策

当 OAuth 客户端只能与一个授权 服务器交互时,不需要混淆 防御。然而,在 OAuth 客户端与两个 或更多授权服务器交互的场景中,客户端必须防止 混淆攻击。下面讨论两种 不同方法。

对于两种防御,客户端必须针对每个授权请求存储 它发送授权请求到的 颁发者,并将该信息绑定到用户 代理。颁发者通过关联的元数据,作为 流程中要使用的授权端点和令牌端点组合的抽象 标识符。如果颁发者标识符不可用(例如,如果既未使用 OAuth 授权服务器元数据 [RFC8414],也未使用 OpenID Connect Discovery [OpenID.Discovery]),则可以 改用该元组的其他唯一标识符或该元组本身。 为简洁起见,下面将此类特定于部署的标识符 归入颁发者(或颁发者标识符)。

需要注意的是,仅存储授权服务器 URL 不足以识别 混淆攻击。攻击者可能将未被攻陷的授权服务器的 授权端点 URL 声明为 “自己的”授权服务器 URL,但声明一个受其自己 控制的令牌端点。

4.4.2.1. 通过颁发者标识进行 混淆防御

此防御要求授权服务器在授权响应中将 其颁发者标识符 发送给客户端。客户端收到授权 响应时,必须将收到的颁发者 标识符与已存储的 颁发者标识符进行比较。如果不匹配,客户端必须中止 交互。

可通过不同方式将此颁发者标识符 传输给客户端:

  • 颁发者信息可以 例如通过单独的响应参数 iss 传输,该参数定义于 [RFC9207]
  • 当使用 OpenID Connect 且授权 响应中返回 ID Token 时,客户端可以评估 ID Token 中的 iss claim。

在这两种情况下,iss必须按照 [RFC9207] 进行评估。

虽然此防御可能需要部署新的 OAuth 功能来传输 颁发者信息,但它是一种稳健且相对简单的混淆 防御。

4.4.2.2. 通过不同的重定向 URI 进行 混淆防御

对于此防御,客户端必须 为其交互的每个颁发者使用不同的重定向 URI。

客户端必须通过将该颁发者的不同重定向 URI 与接收授权响应的 URI 进行比较,检查 授权响应是否来自正确 颁发者。如果不匹配,客户端 必须中止该流程。

虽然此防御建立在现有 OAuth 功能之上,但它不能用于 客户端只为使用许多不同 颁发者而注册一次的场景(例如某些开放银行方案),并且由于与 客户端注册紧密集成,自动部署更困难。

此外,攻击者可能通过在“诚实”的授权服务器上注册一个新客户端来绕过 此防御提供的保护,所使用的 redirect URI 是客户端分配给攻击者授权服务器的 URI。然后,攻击者可以按照上述方式运行 攻击,将 客户端 ID 替换为其新创建客户端的客户端 ID。

因此,只有在其他选项不可用时,才应该 使用此防御。

4.5. 授权码 注入

已获得授权响应中所含授权码访问权限的攻击者 (见 第 3 节 中的 攻击者 (A3)),可以尝试将该 授权码兑换为访问令牌,或以其他方式使用 授权码。

如果授权码是为公共客户端创建的, 攻击者可以将授权码发送到 授权服务器的令牌端点,从而获得访问令牌。该攻击已在 [RFC6819] 第 4.4.1.1 节 中描述。

对于机密客户端,或在某些特殊情况下,攻击者可以 执行授权码注入攻击,如下所述。

在授权码注入攻击中,攻击者试图将 被盗授权码注入到攻击者自己与客户端的会话中。其 目标是将攻击者在客户端的会话与受害者的 资源或身份关联起来,从而让攻击者至少获得对 受害者资源的有限访问。

除了绕过机密客户端的客户端身份认证之外,此攻击的其他 用例包括:

  • 攻击者想访问此特定 客户端中的某些功能。例如,攻击者想要在某个应用或某个网站上冒充其 受害者。
  • 授权服务器或资源服务器被限制在某些 网络中,攻击者无法直接访问这些网络。

除这些特殊情况外,当授权码是为公共客户端创建时,授权码注入通常 并不有吸引力,因为如上所述,将授权码 发送到令牌端点是一种更简单且更强大的攻击。

4.5.1. 攻击 描述

授权码注入攻击的工作方式如下:

  1. 攻击者获得一个授权码(见 第 3 节 中的 攻击者 (A3))。对于攻击的其余部分, 只需要 Web 攻击者 (A1) 的能力。
  2. 攻击者从自己的设备上,使用合法客户端启动常规 OAuth 授权 流程。
  3. 在授权服务器对 合法客户端的响应中,攻击者将新创建的授权码替换为被盗的 授权码。由于此响应经过攻击者的 设备,攻击者可以使用任何能够拦截并操纵 授权响应的工具来实现这一点。攻击者无需控制 网络。
  4. 合法客户端将授权码发送到授权 服务器的令牌 端点,同时发送 redirect_uri、客户端的客户端 ID 和 客户端秘密(或其他客户端身份认证方式)。
  5. 授权服务器检查客户端秘密、授权 码是否颁发给该特定客户端,以及实际 重定向 URI 是否与 redirect_uri 参数匹配(见 [RFC6749])。
  6. 所有检查均成功,授权服务器向客户端颁发访问令牌和 其他令牌。攻击者现在已将自己 与合法客户端的会话同受害者的资源 和/或身份关联起来。

4.5.2. 讨论

显然,如果授权码颁发给了 另一个客户端 ID,例如攻击者建立的客户端,则 check-in 步骤(步骤 5)会失败。如果授权码已被 合法用户兑换且仅可使用一次,该检查 也会失败。

如果授权服务器存储了授权请求中使用的 完整重定向 URI,并将其与 redirect_uri 参数进行比较,也应能检测到尝试注入通过被操纵重定向 URI 获得的授权码。

[RFC6749] 第 4.1.3 节 要求授权服务器

如果初始授权 请求中包含了 "redirect_uri" 参数(如第 4.1.1 节所述), 则确保 "redirect_uri" 参数存在; 如果包含该参数,则确保 它们的值完全相同。

第 4.5.1 节 所述的攻击场景中,合法 客户端会使用其始终用于 授权请求的正确重定向 URI。但此 URI 不会与攻击者使用的被篡改的 重定向 URI 匹配(否则重定向不会 落到攻击者页面)。因此,授权服务器会检测到 攻击并拒绝兑换授权码。

如果满足某些条件,此检查还可以检测尝试注入从同一客户端 在另一台设备上的另一个实例获得的授权 码:

  • 重定向 URI 本身包含 nonce 或其他一次性使用的秘密数据,并且
  • 客户端已将此数据绑定到该 特定客户端实例。

但是,这种方法与在授权端点强制执行精确 redirect URI 匹配的理念相冲突。此外,据观察, 提供者经常忽略此阶段的 redirect_uri 检查 要求,可能是因为从规范文本看它似乎并非 安全关键。

其他提供者只是将 redirect_uri 参数与已注册的重定向 URI 模式进行模式匹配。这避免了 授权服务器为每个事务存储实际 redirect URI 与相应授权码之间的关联。然而, 这种检查显然不符合 规范的意图,因为它没有考虑被篡改的重定向 URI。因此, 任何尝试注入使用合法客户端的 client_id 获得的授权码,或通过在另一台设备上利用合法 客户端获得的授权码,都不会在相应 部署中被检测到。

还假定 [RFC6749] 第 4.1.3 节 中定义的要求会增加 客户端实现复杂性,因为客户端 需要存储或重构用于调用 令牌端点的正确重定向 URI。

用于客户端身份认证的非对称方法并不能阻止此 攻击,因为 合法客户端会在令牌端点进行身份认证。

因此,本文档建议改为使用下面描述的一种 机制,在某个事务的上下文中,将每个授权 码绑定到某台设备上(或某个 用户代理中)的特定客户端实例。

4.5.3. 对策

有两种良好的技术方案可将授权 码绑定到客户端 实例,如下所示。

4.5.3.1. PKCE

[RFC7636] 中规定的 PKCE 机制可用作对策 (尽管它最初设计用于保护原生应用)。当 攻击者尝试注入授权码时, code_verifier 检查会失败:客户端使用其正确的 verifier,但该 授权码关联的是与此 verifier 不匹配的 code_challenge

PKCE 不仅能防护授权码 注入攻击,还能保护为公共客户端创建的授权码:PKCE 确保 攻击者在不知道 code_verifier 的情况下,无法在 授权服务器的令牌端点兑换被盗授权码。

4.5.3.2. Nonce

OpenID Connect 现有的 nonce 参数可以 防护授权 码注入攻击。nonce 值一次性使用,并由 客户端创建。客户端应将其绑定到用户代理会话,并在初始请求中发送给 OpenID Provider (OP)。 OP 会将收到的 nonce 值放入作为令牌端点授权码交换一部分所颁发的 ID Token 中。 如果攻击者在授权响应中注入 授权码,则客户端会话中的 nonce 值与从令牌 端点收到的 ID Token 中的 nonce 值将不匹配,攻击会被 检测到。这里的假设是,攻击者无法获得受害者设备上的用户代理 状态(攻击者是从该设备窃取相应 授权码的)。

需要注意的是,只有当客户端 正确检查从令牌端点获得的 ID Token 中的 nonce 参数,并且在该检查成功之前不使用任何 颁发的令牌时,此对策才有效。更准确地说,使用 nonce 参数防护自身免受 code injection 的客户端

  1. 必须验证从令牌端点获得的 ID Token 中的 nonce, 即使已从授权响应获得了另一个 ID Token (例如 response_type=code+id_token),并且
  2. 必须确保,除非且直到该检查 成功,否则所有令牌(ID Tokens 和访问令牌)都被忽略,并且不得用于任何其他 目的。

需要注意的是,nonce 无法保护 公共客户端的授权码,因为攻击者无需执行授权码 注入攻击。相反,攻击者可以直接携带 被盗授权码调用令牌端点。

4.5.3.3. 其他 解决方案

其他方案,例如将 state 绑定到 授权码、使用密码学手段对授权码进行发送者约束,或使用每实例客户端凭据, 都是可以设想的,但缺乏支持,并会带来新的安全要求。

PKCE 是 OAuth 客户端最显而易见的方案,因为它 在撰写时已经可用,而 nonce 则 适用于 OpenID Connect 客户端。

4.5.4. 局限性

如果攻击者能够修改受害者授权请求中使用的 noncecode_challenge 值, 就可以绕过上述对策。攻击者可以将这些值 修改为与其自己会话中客户端所选择的值相同, 即上面攻击 步骤 2 中的值。 (这要求受害者与客户端的会话 在攻击者与客户端启动会话之后才开始。)如果攻击者随后能够从受害者捕获 授权码,则即使使用了 PKCE 或 nonce,攻击者也能够在 步骤 3 中注入被盗授权码。

这种攻击复杂,并要求攻击者与受害者会话之间有紧密交互。 尽管如此,仍需要采取措施防止 攻击者读取授权响应的内容, 如第 4.14.24.34.44.11 节所述。

4.6. 访问令牌注入

在访问令牌注入攻击中,攻击者试图将 被盗访问令牌注入到一个合法客户端中(该客户端不受 攻击者控制)。这通常发生在攻击者想要 利用泄露的访问令牌,在某个客户端中冒充用户时。

为实施该攻击,攻击者使用隐式授权与 客户端启动 OAuth 流程,并修改授权 响应,将授权 服务器颁发的访问令牌替换掉,或直接伪造一个包含 泄露访问令牌的授权服务器响应。由于响应包含 客户端为该特定事务生成的 state 值,客户端 不会将响应视为 CSRF 攻击,并会使用 攻击者注入的访问令牌。

4.6.1. 对策

在纯 OAuth 流程中无法检测这种注入攻击, 因为令牌在颁发时没有绑定到 事务或特定用户代理。

在 OpenID Connect 中,可以缓解该攻击,因为授权 响应还包含一个带有 at_hash claim 的 ID Token。因此,攻击者 需要同时替换响应中的访问令牌和 ID Token。 攻击者无法伪造 ID Token,因为它经过签名或加密 并具有身份认证。攻击者也无法注入与 被盗访问令牌匹配的泄露 ID Token,因为泄露 ID Token 中的 nonce claim (以极高概率)会包含与授权响应中预期值不同的值。

注意,仍然需要进一步保护,例如发送者约束访问令牌, 以防止攻击者直接在资源 端点使用访问令牌。

第 2.1.2 节 中的建议由此得出。

4.7. 跨站请求 伪造

攻击者可能试图在受害者设备上向 合法客户端的重定向 URI 注入请求,例如,使 客户端访问攻击者控制下的资源。这是 一种称为跨站请求伪造 (CSRF) 的攻击变体。

4.7.1. 对策

长期确立的对策是,客户端在 state 参数中传递一个随机 值,也称为 CSRF Token,用于按所述方式将请求和 重定向 URI 关联到用户代理会话。 此对策在 [RFC6819] 第 5.3.5 节 中详细描述。PKCE 或 OpenID Connect nonce 值提供相同的保护。

在使用 PKCE 而不是 statenonce 进行 CSRF 保护时,需要 注意:

  • 客户端必须确保 授权服务器支持 PKCE,然后才能使用 PKCE 进行 CSRF 保护。如果授权服务器不支持 PKCE, 则必须使用 statenonce 进行 CSRF 保护。

  • 如果 state 用于携带应用 状态,且其内容完整性值得关注,则客户端必须保护 state 免遭 篡改和调换。可通过将 state 内容绑定到浏览器会话和/或对 state 值进行签名/加密来实现这一点。过期的 Internet-Draft [JWT-ENCODED-STATE] 中讨论了一个示例。

因此,授权服务器必须 提供一种方式来检测其对 PKCE 的支持。[RFC8414] 所规定的授权服务器元数据 建议使用,但授权服务器可以改为提供一种 特定于部署的方式,以确保或确定 PKCE 支持。

即使存在能够读取授权响应的攻击者(见 第 3 节 中的 攻击者 (A3)),PKCE 也能对 CSRF 攻击提供稳健保护。 当使用 state,或在授权响应中返回 ID Token(例如 response_type=code+id_token)时,攻击者要么获知 state 值并 能够将其重放到伪造的授权响应中,要么可以从 ID Token 中提取 nonce, 并在向授权服务器发起的新请求中使用它,以铸造具有相同 nonce 的 ID Token。 随后可将新的 ID Token 用于 CSRF 攻击。

4.8. PKCE 降级攻击

支持 PKCE 但未要求所有流程 强制使用 PKCE 的授权服务器,可能容易受到 PKCE 降级攻击。

此攻击的第一个前提是授权请求中存在一个 攻击者可控制的 标志,用于为特定流程启用或禁用 PKCE。 code_challenge 参数的存在或缺失很适合此目的,即授权服务器在该 参数存在于授权请求中时启用并强制 PKCE,但在 参数缺失时不强制 PKCE。

此攻击的第二个前提是客户端完全未使用 state (例如,因为客户端依赖 PKCE 来防止 CSRF),或客户端 未正确检查 state

粗略地说,此攻击是 CSRF 攻击的一个变体。攻击者 达成的目标与 第 4.7 节 中描述的攻击相同:攻击者将绑定到攻击者 资源的授权码(以及由此得到的访问令牌) 注入到受害者与客户端之间的会话中。

4.8.1. 攻击 描述

  1. 用户已在某个授权服务器上使用某个客户端启动 OAuth 会话。在 授权请求中,客户端已设置参数 code_challenge=hash(abc) 作为 PKCE code challenge(其中哈希 函数和参数编码按 [RFC7636] 定义)。客户端现在 等待从用户浏览器接收授权响应。
  2. 为实施攻击,攻击者使用自己的设备与目标客户端启动 授权流程。客户端现在在授权 请求中使用另一个 PKCE code challenge,例如 code_challenge=hash(xyz)。 攻击者拦截该请求,并从请求中移除整个 code_challenge 参数。由于此步骤在 攻击者设备上执行,攻击者可以完全访问请求内容, 例如使用浏览器调试工具。
  3. 如果授权服务器允许不使用 PKCE 的流程, 它将创建一个未绑定到任何 PKCE code challenge 的 授权码。
  4. 攻击者现在将用户浏览器重定向到一个 授权响应 URL,该 URL 包含攻击者与授权 服务器会话的授权码。
  5. 用户浏览器将授权码发送给 客户端,客户端现在会 尝试在授权服务器处将该授权码兑换为访问令牌。客户端会 在令牌请求中发送 code_verifier=abc 作为 PKCE code verifier。
  6. 由于授权服务器看到此授权码没有绑定到任何 PKCE code challenge,因此它不会检查 code_verifier 参数的存在或内容。它会向用户控制下的客户端 颁发访问令牌(该令牌属于 攻击者的资源)。

4.8.2. 对策

正确使用 state 会阻止此攻击。 然而,实践表明 许多 OAuth 客户端没有正确使用或检查 state

因此,授权服务器必须 缓解此攻击。

注意,从授权服务器的角度看,在上述攻击中, 令牌端点收到了 code_verifier 参数,尽管在颁发该授权码的 OAuth 流程的授权请求中并不存在 code_challenge 参数。

可利用这一事实缓解该攻击。[RFC7636] 已经规定

  • 支持 PKCE 的授权服务器 必须检查授权请求中是否包含 code challenge,并将该信息绑定到所颁发的 授权码;并且
  • 当授权码到达令牌端点时, 如果为其颁发该授权码的授权请求中存在 code_challenge, 则令牌请求中必须存在有效的 code_verifier

除此之外,为防止 PKCE 降级攻击,授权 服务器必须确保: 如果授权请求中没有 code_challenge,则拒绝包含 code_verifier 的令牌端点请求。

强制使用 PKCE(一般性或针对特定客户端)的授权服务器 隐含实现了这一安全措施。

4.9. 资源服务器上的访问令牌泄露

在某些情况下,访问令牌可能从资源服务器泄露。

4.9.1. 通过假冒资源服务器进行访问令牌 钓鱼

攻击者可以建立自己的资源服务器,并诱骗客户端 向其发送对其他资源服务器有效的访问令牌 (见 第 3 节 中的攻击者 (A1)(A5))。如果客户端向 这个假冒资源服务器发送有效访问令牌,攻击者随后可能使用该 令牌代表资源所有者访问其他服务。

此攻击假定客户端在开发时并未绑定到某一个特定资源 服务器(及其 URL),而是客户端实例在运行时 获得资源服务器 URL。 这种后期 绑定常见于客户端使用实现标准化 API 的服务的情形 (例如电子邮件、日历、电子健康或开放银行),并且客户端由用户或 管理员配置。

4.9.2. 受陷的 资源服务器

攻击者可以攻陷资源服务器,以访问相应部署的 资源。此类攻陷可能从 对系统的部分访问(例如其日志文件)到对相应服务器的完全 控制不等;在后一种情况下,所有控制都可能被 绕过,所有资源都可以被 访问。攻击者还能够获得受陷系统中持有的其他访问 令牌,这些令牌可能对访问其他资源服务器有效。

通过加固和监控服务器 系统来防止服务器被攻陷,被视为标准运维流程, 因此超出 本文档范围。 第 4.9 节 聚焦于 OAuth 相关攻陷的影响以及捕获访问令牌的重放。

4.9.3. 对策

实现者应考虑采取以下措施,以 应对恶意行为者对访问令牌的重放:

  • 第 4.10.1 节 所述,应该 使用发送者约束访问令牌,以防止攻击者在其他资源服务器上重放访问 令牌。如果攻击者仅对受陷系统具有部分 访问权限,例如对 Web 服务器日志具有只读访问权限,发送者约束访问令牌也可能防止 在受陷系统上重放。
  • 第 4.10.2 节 所述,应该 使用受众限制,以防止在其他资源 服务器上重放捕获的访问令牌。
  • 资源服务器必须像处理其他敏感秘密一样处理访问令牌, 不得以明文存储或传输它们。

第一和第二项建议也适用于访问令牌泄露的其他场景 (见 第 3 节 中的 攻击者 (A5))。

4.10. 被盗访问令牌的 滥用

访问令牌可以通过多种方式被攻击者窃取,例如 通过第 4.14.24.34.44.9 节所述的攻击。其中一些攻击可通过 相应章节中描述的 特定安全措施来缓解。 然而,在某些情况下,这些措施不足,或未被正确 实现。因此,授权服务器应该确保 访问令牌按下文所述进行发送者约束和受众限制。 架构和性能原因可能会 阻碍某些部署使用这些措施。

4.10.1. 发送者约束访问令牌

顾名思义,发送者约束访问令牌将 访问令牌的适用范围限定到某个发送者。该发送者有 义务证明其知道某个秘密,作为该令牌在资源服务器处被接受的前提。

典型流程如下:

  1. 授权服务器将数据与访问 令牌关联,以将该特定令牌绑定到某个客户端。该绑定 可以利用客户端身份,但在大多数情况下,授权服务器会利用 客户端已知的 密钥材料(或由密钥材料派生的数据)。
  2. 此密钥材料必须以某种方式分发。要么 密钥 材料在授权服务器创建绑定之前已存在,要么 授权服务器创建临时密钥。预先存在的密钥材料 在不同方法中的分发方式各不相同。例如, 可以使用 X.509 证书,在这种情况下,分发 会在登记过程中显式发生。或者,密钥 材料在 TLS 层创建并分发,在这种 情况下,它可能在 TLS 连接建立期间自动发生。
  3. 资源服务器必须实现实际的 持有证明检查。这 通常在应用层完成,并经常绑定到传输层提供的特定 材料(例如 TLS)。资源服务器还必须 确保持有证明不可被重放。

OAuth 工作组已经定义并在实践中使用了两种 使用持有证明的发送者约束访问令牌方法:

  • "OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens" [RFC8705]:本文档中指定的 方法允许将相互 TLS 同时用于客户端 身份认证和发送者约束访问令牌。对于 发送者约束访问令牌,客户端通过其 公钥指纹向资源服务器标识自己。在处理访问令牌请求期间, 授权服务器从 TLS 栈获取客户端公钥,并将其指纹与相应的访问 令牌关联。资源服务器同样从 TLS 栈获取公钥,并将其指纹与 访问令牌关联的 指纹进行比较。
  • "OAuth 2.0 Demonstrating Proof of Possession (DPoP)" [RFC9449]: DPoP 概述了一种 应用层机制,用于对访问令牌和刷新 令牌进行发送者约束。它使用 基于公钥/私钥对和 应用层签名的持有证明。DPoP 可用于公共客户端, 并且在机密客户端的情况下,可与任何 客户端身份认证方法结合使用。

注意,当攻击者获得令牌和密钥材料时, 发送者约束令牌的安全性会被削弱。这一点尤其适用于 被破坏的客户端软件和跨站 脚本攻击(当客户端运行在浏览器中时)。如果 密钥材料受到硬件或软件安全模块保护,或 只能间接访问(例如在 TLS 栈中),发送者约束 令牌至少能在客户端 离线时(即安全模块或接口对攻击者不可用时)防止令牌使用。 这既适用于访问令牌,也适用于刷新 令牌(见 第 4.14 节)。

4.10.2. 受众限制访问令牌

受众限制实质上将访问令牌限制到 特定资源服务器。授权服务器将 访问令牌与特定资源服务器关联,然后资源 服务器应验证预期受众。如果访问令牌未通过 预期受众验证,则资源服务器拒绝 服务相应请求。

一般而言,受众限制会限制令牌 泄露的影响。 在假冒资源服务器的情况下,它还可能(如下所述) 防止钓鱼得到的访问令牌在 合法资源服务器处被滥用。

受众可以使用逻辑名称或 物理地址(如 URL)表示。为防止钓鱼,必须使用客户端将请求发送到的实际 URL。 在钓鱼情况下,该 URL 会指向假冒资源 服务器。如果攻击者试图在 合法资源服务器(其 URL 不同)使用访问令牌,资源 服务器将检测到不匹配(错误受众)并拒绝服务 该请求。

在授权服务器知道所有 资源服务器 URL 的部署中,授权服务器可以直接拒绝为未知资源服务器 URL 颁发 访问令牌。

为使其生效,客户端需要告知授权服务器其预期的 资源服务器。[RFC8707] 中的机制可用于此目的,或可将该 信息编码到 scope 值中([RFC6749] 第 3.3 节)。

除了 URL,也可以使用 资源服务器 X.509 证书的指纹作为受众值。该 变体还允许检测使用从不同 CA 获得的有效 TLS 证书来伪造合法 资源服务器 URL 的尝试。它也可被视为一种隐私收益,因为可以向授权服务器隐藏 资源服务器 URL。

受众限制似乎更容易使用,因为它不 要求客户端侧进行任何密码学操作。不过,由于每个访问令牌都 绑定到特定资源服务器,客户端在访问多个资源 服务器时,也需要获得单个面向资源服务器的访问令牌。 ([RFC8707] 中指定的 Resource Indicators 可帮助实现这一点。) [TOKEN-BINDING] 也具有相同属性,因为 不同 token-binding ID 必须与访问令牌关联。另一方面,使用 OAuth 2.0 的相互 TLS [RFC8705] 允许客户端在多个资源服务器上使用 同一访问令牌。

应注意,受众限制——或者更一般地说,客户端向授权服务器 表明其希望在何处使用 访问令牌——具有超出令牌泄露防护范围的额外好处。 它们允许授权服务器创建一个不同的访问令牌,其格式和内容 专门为相应服务器铸造。 在使用结构化访问令牌的部署中,这具有巨大的功能和隐私 优势。

4.10.3. 讨论: 通过元数据防止泄露

授权服务器可以向客户端提供有关 可安全使用其访问 令牌的位置的额外信息。下面讨论此方法及其不被推荐的原因。

最简单的形式中,这要求授权服务器 发布其已知资源服务器列表,以下示例使用 非标准授权服务器元数据参数 resource_servers 进行说明:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "issuer":"https://server.somesite.example",
  "authorization_endpoint":
    "https://server.somesite.example/authorize",
  "resource_servers":[
    "email.somesite.example",
    "storage.somesite.example",
    "video.somesite.example"
  ]
  ...
}

授权服务器也可以在 令牌响应中返回访问令牌适用的 URL,示例如下,使用 非标准返回参数 access_token_resource_server

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "access_token_resource_server":
    "https://hostedresource.somesite.example/path1",
...
}

这种缓解策略将依赖客户端执行 安全策略,并且只将访问令牌发送到合法 目的地。OAuth 相关安全研究结果(例如见 [research.ubc][research.cmu])表明,相当大一部分客户端实现没有或未能正确 实现安全控制,例如 state 检查。因此, 依赖客户端来防止访问令牌钓鱼也很可能失败。 此外,考虑到客户端与授权服务器和资源服务器的比例, 将尽可能多的 安全相关逻辑移到这些服务器上,被认为是更可行的方法。 显然,客户端 必须为整体安全作出贡献。然而,存在替代 对策,如第 4.10.14.10.2 节所述,它们在相关参与方之间提供了 更好的平衡。

4.11. 开放重定向

当授权服务器或客户端具有开放重定向器时,可能发生以下 攻击。此类端点有时会被实现出来, 例如用于在用户随后被重定向到外部 网站之前显示一条消息,或将用户重定向回他们在被中断前(例如被登录提示中断前) 打算访问的 URL。

4.11.1. 作为开放 重定向器的客户端

客户端不得暴露开放 重定向器。攻击者可能利用开放 重定向器生成指向客户端的 URL,并用它们 外泄授权码和访问令牌,如 第 4.1.2 节 所述。另一种 滥用情形是生成看似指向客户端的 URL。 这可能诱骗用户信任该 URL 并在浏览器中访问它。这可被用于钓鱼。

为防止开放重定向,客户端只有在 目标 URL 被允许,或请求的来源和完整性能够 进行身份认证时,才应重定向。OWASP [owasp.redir] 描述了针对开放重定向的对策。

4.11.2. 作为开放重定向器的授权 服务器

与客户端一样,攻击者也可能试图利用用户对 授权服务器(尤其是其 URL)的信任来执行 钓鱼攻击。OAuth 授权服务器经常将用户重定向 到其他网站(客户端),但必须安全地这样做。

[RFC6749] 第 4.1.2.1 节 已经通过规定授权服务器不得client_idredirect_uri 组合无效的情况下自动 重定向用户代理,来防止开放重定向。

然而,攻击者也可以利用正确注册的 重定向 URI 来执行钓鱼攻击。例如,攻击者可以 通过动态客户端注册 [RFC7591] 注册客户端,并执行以下攻击之一:

  1. 故意发送错误的授权请求,例如 使用无效的 scope 值,从而指示授权服务器将 用户代理重定向到其钓鱼站点。
  2. 故意发送一个有效授权请求,其中 client_idredirect_uri 由攻击者控制。用户完成身份认证后, 授权服务器提示用户同意该请求。如果 用户注意到该请求存在问题并拒绝请求, 授权服务器仍会将用户代理重定向到钓鱼站点。在 这种情况下,无论用户采取什么操作,用户代理都会被重定向到钓鱼站点。
  3. 故意发送有效的静默身份认证请求 (prompt=none), 其中 client_idredirect_uri 由攻击者控制。在这种 情况下,授权服务器会自动将用户代理重定向到 钓鱼站点。

授权服务器必须采取 预防措施来防止这些威胁。授权服务器必须始终 先对用户进行身份认证,并且除静默身份认证 用例外,在重定向 用户之前,在需要时提示用户输入凭据。基于其风险评估,授权服务器需要决定是否可以信任 该重定向 URI。它可以考虑通过内部或某些外部服务完成的 URI 分析, 以评估该 URI 背后内容的可信度和 可信性,以及重定向 URI 和其他客户端数据的来源。

只有在信任该 重定向 URI 时,授权服务器应该才自动重定向用户代理。 如果 URI 不被信任,授权服务器可以告知用户,并依赖 用户作出正确决定。

4.12. 307 重定向

在授权端点,一个典型协议流程是授权服务器 提示用户在表单中输入凭据,然后该表单 (使用 HTTP POST 方法)提交回授权 服务器。授权服务器检查凭据,并在成功后将 用户代理重定向到客户端的重定向端点。

[RFC6749] 中,HTTP 状态码 302 (Found) 用于此目的,但 “允许通过 user-agent 可用的任何其他方法完成此 重定向”。当使用状态码 307 进行重定向时,用户代理会通过 HTTP POST 将用户凭据发送给客户端。

这会将敏感凭据披露给客户端。如果客户端 是恶意的,它可以使用这些凭据在授权服务器处冒充用户。

该行为可能出乎开发者意料,但它在 [RFC9110] 第 15.4.8 节 中有定义。此状态码 (307) 不要求用户 代理将 POST 请求改写为 GET 请求,从而丢弃 POST 请求正文中的表单数据。

在 HTTP 标准 [RFC9110] 中,只有状态码 303 明确强制将 HTTP POST 请求改写为 HTTP GET 请求。 对于所有其他状态码,包括流行的 302,用户 代理可以选择不将 POST 改写为 GET 请求,从而 导致用户凭据泄露给客户端。(然而在实践中,大多数 用户代理只会对 307 重定向表现出这种行为。)

因此,重定向可能包含 用户凭据的请求的授权服务器不得使用 HTTP 307 状态码进行重定向。如果 对此类请求使用 HTTP 重定向(而不是例如 JavaScript),则授权服务器应该使用 HTTP 状态码 303 (See Other)。

4.13. TLS 终止型 反向代理

HTTP 应用的常见部署架构是将 应用服务器隐藏在一个终止 TLS 连接并将传入请求分派到相应 应用服务器节点的反向代理后面。

本节强调这种部署 架构中与 OAuth 相关的一些攻击角度,并给出 安全控制建议。

在某些情况下,反向代理需要将安全相关 数据传递给上游应用服务器以进一步处理。 示例包括请求发起者的 IP 地址、token-binding ID,以及已认证的 TLS 客户端证书。此类数据通常通过添加到上游请求中的 HTTP 标头传递。 虽然这些标头通常是自定义的、特定于应用的标头,但 [RFC9440] 中定义了用于客户端证书和客户端证书链的标准化标头 字段。

如果反向代理透传任何来自 外部的标头,攻击者可能会尝试通过代理直接向应用服务器发送伪造的标头 值,以此绕过 安全控制。例如,反向代理接受 X-Forwarded-For 标头并仅 添加入站请求来源(使其成为列表)是一种标准 做法。根据应用服务器中执行的逻辑,攻击者可能 仅向该标头添加一个被允许的 IP 地址,从而使保护失效。

因此,反向代理必须净化任何 入站请求,以确保与 应用服务器安全相关的所有标头值的真实性和完整性。

如果攻击者能够访问代理与应用服务器之间的内部网络, 攻击者也可能尝试绕过现有 安全控制。因此,确保通信实体的真实性至关重要。此外, 反向代理与应用服务器之间的通信链路 必须受到保护,以防消息被窃听、注入和重放。

4.14. 刷新令牌 保护

刷新令牌是一种便捷且用户友好的方式,用于获得新的访问 令牌。它们也有助于 OAuth 的安全性,因为它们允许授权服务器颁发 生命周期短且 scope 缩小的访问令牌,从而降低 访问令牌泄露的潜在影响。

4.14.1. 讨论

刷新令牌对攻击者具有吸引力,因为它们 代表授予 某个客户端的完整访问范围,并且没有进一步约束到特定 资源。 如果攻击者能够外泄并成功重放 刷新令牌,则攻击者将能够铸造访问令牌,并代表资源所有者使用 它们访问资源服务器。

[RFC6749] 已通过以下要求提供了稳健的基线保护:

  • 刷新令牌在传输和存储中的 机密性,
  • 在授权服务器与客户端之间 通过 TLS 保护的连接传输刷新令牌,
  • 授权服务器在可能时维护并 检查刷新令牌与某个客户端之间的绑定,并在令牌刷新期间对该客户端进行身份认证, 并且
  • 刷新令牌不能被生成、 修改或猜测。

[RFC6749] 还通过定义相应的错误码和响应行为,为进一步的 (特定于实现的)安全措施奠定了基础,例如刷新令牌过期和 撤销,以及刷新令牌轮换。

本规范给出了超出 [RFC6749] 范围的建议和澄清。

4.14.2. 建议

授权服务器必须基于风险评估 确定是否向某个客户端颁发刷新令牌。如果 授权服务器决定不颁发刷新令牌,则客户端 可以通过利用其他授权类型(例如 授权码授权类型)来获得新的访问令牌。在这种情况下,授权 服务器可以利用 cookie 和持久授权来优化用户 体验。

如果颁发刷新令牌,则这些刷新令牌必须绑定到 资源所有者同意的 scope 和资源服务器。 这样做是为了防止合法客户端进行权限提升,并降低 刷新令牌泄露的影响。

对于机密客户端,[RFC6749] 已经要求刷新 令牌只能由其被颁发给的客户端使用。

对于公共客户端,授权服务器必须利用以下方法之一 检测恶意行为者对刷新令牌的重放:

  • 发送者约束刷新令牌:授权服务器 将刷新令牌以密码学方式绑定到某个客户端 实例,例如,通过利用 [RFC8705][RFC9449]
  • 刷新令牌轮换:授权服务器在每个访问令牌刷新响应中颁发新的 刷新令牌。 先前的刷新令牌被作废,但授权服务器保留有关 关系的信息。如果刷新 令牌被泄露,并随后被攻击者和合法客户端同时使用, 其中一方会提交一个已作废的 刷新令牌,这会通知授权服务器发生了 泄露。授权服务器无法确定是哪一方 提交了无效刷新令牌,但会撤销 活动刷新令牌。这会阻止攻击,代价是迫使 合法客户端获得新的授权 grant。

    实现说明:刷新令牌 所属的 grant 可以被编码到刷新令牌本身。这可以使 授权服务器高效确定刷新 令牌所属的 grant,并由此确定所有需要被撤销的刷新令牌。 在这种情况下,授权服务器必须确保 刷新令牌值的完整性,例如, 使用签名。

在发生安全事件时,授权服务器可以自动撤销刷新 令牌,例如:

  • 密码变更,或
  • 在授权服务器注销。

如果客户端已有一段时间不活跃, 即刷新令牌已有一段时间未用于获得新的访问 令牌,则刷新令牌应该过期。过期时间由 授权服务器自行决定。它可以是全局值,也可以基于 客户端策略或与刷新令牌关联的 grant (及其敏感性)来确定。

4.15. 客户端冒充 资源所有者

资源服务器可以基于访问令牌所代表的 资源所有者身份,或基于客户端凭据授权中客户端的身份来作出访问控制决策。 例如,[RFC9068](JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens)描述了一种用于 访问令牌的数据结构,其中包含按如下方式定义的 sub claim:

在通过涉及资源所有者的 grant 获得访问令牌的情况下,例如授权码授权,"sub" 的值 应该对应于资源 所有者的主体标识符。 在通过不涉及 资源所有者的 grant 获得访问令牌的情况下,例如客户端凭据授权, "sub" 的值应该对应于授权服务器用来指示客户端应用的标识符。

如果两种选项都可能出现,资源服务器可能会将客户端 身份误认为资源所有者的身份。例如,如果客户端能够在授权服务器注册时选择 自己的 client_id,恶意客户端可能将其设置为标识资源所有者的值 (例如,如果使用 OpenID Connect,则为 sub 值)。如果资源服务器无法正确 区分通过涉及资源 所有者获得的访问令牌和不涉及资源所有者获得的访问令牌, 客户端可能会意外地访问属于资源 所有者的资源。

此攻击不仅可能影响使用 [RFC9068] 的实现, 也可能影响类似的定制解决方案。

4.15.1. 对策

如果客户端 ID 和用户标识符存在公共 命名空间,例如上文 第 4.15 节 中来自 [RFC9068]sub claim 示例,则授权服务器不应该允许 客户端影响其 client_id 或 任何其他可能导致与真实资源所有者混淆的 claim。 在无法避免的情况下,授权服务器必须 提供 其他手段,使资源服务器能够区分这两类 访问令牌。

4.16. 点击劫持

[RFC6819] 第 4.4.1.9 节 所述, 授权请求容易受到点击劫持攻击,也称为用户界面重绘攻击。 在此类攻击中,攻击者将授权端点用户界面嵌入到 一个无害上下文中。用户以为自己正在与该上下文交互, 例如点击按钮,却无意中与授权 端点用户界面交互。反过来也可以做到:用户 以为正在与授权端点交互,却可能无意中将 密码输入到攻击者提供的输入字段中,该字段覆盖在原始用户 界面之上。点击劫持攻击可以设计成让用户几乎 无法察觉,例如,使用几乎不可见的 iframe 覆盖在 其他元素之上。

攻击者可以利用此向量获取用户的身份认证 凭据, 更改授予客户端的访问 scope,并可能访问 用户资源。

授权服务器必须防止点击劫持 攻击。[RFC6819] 中描述了多种 对策,包括使用 X-Frame-Options HTTP 响应标头字段和 frame-busting JavaScript。除此之外,授权服务器应该还 使用 Content Security Policy (CSP) level 2 [W3C.CSP-2] 或更高版本。

为发挥效果,CSP 必须用于授权端点,并且 如适用,也用于其他用于对用户进行身份认证和 授权客户端的端点(例如设备授权端点、登录 页面、错误页面等)。这会在支持 CSP 的用户代理中防止未授权 origin 的框架嵌入。客户端可以允许被 与其重定向端点所用 origin 不同的其他 origin 嵌入。因此,授权服务器应该允许 管理员为特定客户端配置允许的 origin, 和/或允许客户端动态注册这些 origin。

使用 CSP 允许授权服务器在 单个响应标头字段中指定多个 origin,并使用灵活 模式约束它们(详见 [W3C.CSP-2])。 CSP level 2 通过使用 限制 frame 来源的策略(使用 frame-ancestors) 与限制 HTML 页面上允许执行脚本来源的策略 (使用 script-src)相结合,为防止点击劫持提供了 稳健机制。下面的列表展示了这种策略的一个非规范性 示例:

HTTP/1.1 200 OK
Content-Security-Policy: frame-ancestors https://ext.example.org:8000
Content-Security-Policy: script-src 'self'
X-Frame-Options: ALLOW-FROM https://ext.example.org:8000
...

由于某些用户代理不支持 [W3C.CSP-2],此技术 应该与其他技术结合使用,包括 [RFC6819] 中描述的技术,除非授权服务器明确不支持此类旧版用户代理。 即使在这种情况下,仍应该采用额外 对策。

4.17. 针对浏览器内 通信流程的攻击

如果授权响应通过浏览器内通信 技术(例如 postMessage [WHATWG.postmessage_api])发送,而不是通过 HTTP 重定向发送,则消息 可能会无意中被发送到恶意 origin,或从恶意 origin 注入。

4.17.1. 示例

以下使用浏览器内 通信的攻击非规范性伪代码示例在 [research.rub] 中有描述。

4.17.1.1. 对接收方 origin 限制不足

在通过 postMessage 发送授权响应或令牌响应时,授权服务器将响应发送到通配符 origin "*",而不是客户端 origin。当接收响应的窗口由攻击者控制时, 攻击者可以读取 响应。

window.opener.postMessage(
  {
    code: "ABC",
    state: "123"
  },
  "*" // any website in the opener window can receive the message
)
4.17.1.2. URI 验证不足

在通过 postMessage 发送授权响应或令牌响应时,授权服务器可能不会将 接收方 origin 与重定向 URI 进行检查,而是例如可能将 响应发送到攻击者提供的 origin。这类似于 第 4.1 节 中描述的攻击。

window.opener.postMessage(
  {
    code: "ABC",
    state: "123"
  },
  "https://attacker.example" // attacker-provided value
)
4.17.1.3. 发送方 origin 验证不足后的 注入

期望通过 postMessage 接收授权响应或令牌响应的客户端,可能不会验证消息的发送方 origin。这 可能允许攻击者将授权响应或令牌响应 注入到客户端中。

在恶意注入授权 响应的情况下,该攻击是 第 4.7 节 所述 CSRF 攻击的变体。 第 4.7 节 中描述的 对策同样适用于此攻击。

在恶意注入令牌响应的情况下, 如 第 4.10.1 节 所述的发送者约束 访问令牌,在某些情况下可防止该攻击,但通常仍需要 第 4.17.2 节 中描述的其他 对策。

4.17.2. 建议

在将客户端接收方 origin 与预注册的 origin 进行比较时, 授权服务器必须使用 第 4.1.3 节 中描述的精确字符串匹配。 授权服务器必须将 postMessage 发送到 可信客户端接收方 origin,如以下非规范性示例所示:

window.opener.postMessage(
  {
    code: "ABC",
    state: "123"
  },
  "https://client.example" // use explicit client origin
)

postMessage 中不得使用 "*" 这样的通配符 origin,因为攻击者可以利用它们 将受害者的浏览器内消息泄露到恶意 origin。 这两项措施都有助于防止授权码和 访问令牌泄露(见 第 4.1 节)。

客户端必须防止客户端 接收端点上的浏览器内消息 注入。客户端必须使用精确字符串匹配来比较 浏览器内消息的发起方 origin 与授权 服务器 origin,如以下非规范性示例所示:

window.addEventListener("message", (e) => {
  // validate exact authorization server origin
  if (e.origin === "https://honest.as.example") {
    // process e.data.code and e.data.state
  }
})

由于浏览器内通信流程只是使用了不同的 通信 技术(即 postMessage 而不是 HTTP redirect),因此 第 2.1 节 中列出的保护授权响应的所有措施都 必须同等适用。

5. IANA 考量

本文档没有 IANA 操作。

6. 安全考量

安全考量见第 234 节。

7. 参考文献

7.1. 规范性参考文献

[BCP195]
最佳当前实践 195,<https://www.rfc-editor.org/info/bcp195>
在撰写时,此 BCP 包含以下内容:
Moriarty, K.S. Farrell"弃用 TLS 1.0 和 TLS 1.1"BCP 195RFC 8996DOI 10.17487/RFC8996<https://www.rfc-editor.org/info/rfc8996>
Sheffer, Y.Saint-Andre, P.T. Fossati"安全使用传输层安全性 (TLS) 和数据报传输层安全性 (DTLS) 的建议"BCP 195RFC 9325DOI 10.17487/RFC9325<https://www.rfc-editor.org/info/rfc9325>
[RFC3986]
Berners-Lee, T.Fielding, R.L. Masinter"统一资源标识符 (URI):通用语法"STD 66RFC 3986DOI 10.17487/RFC3986<https://www.rfc-editor.org/info/rfc3986>
[RFC6749]
Hardt, D., Ed."OAuth 2.0 授权 框架"RFC 6749DOI 10.17487/RFC6749<https://www.rfc-editor.org/info/rfc6749>
[RFC6750]
Jones, M.D. Hardt"OAuth 2.0 授权框架:承载令牌用法"RFC 6750DOI 10.17487/RFC6750<https://www.rfc-editor.org/info/rfc6750>
[RFC6819]
Lodderstedt, T., Ed.McGloin, M.P. Hunt"OAuth 2.0 威胁模型和安全 考量"RFC 6819DOI 10.17487/RFC6819<https://www.rfc-editor.org/info/rfc6819>
[RFC7521]
Campbell, B.Mortimore, C.Jones, M.Y. Goland"OAuth 2.0 客户端身份认证和授权 Grant 的断言框架"RFC 7521DOI 10.17487/RFC7521<https://www.rfc-editor.org/info/rfc7521>
[RFC7523]
Jones, M.Campbell, B.C. Mortimore"OAuth 2.0 客户端身份认证和授权 Grant 的 JSON Web Token (JWT) Profile"RFC 7523DOI 10.17487/RFC7523<https://www.rfc-editor.org/info/rfc7523>
[RFC8252]
Denniss, W.J. Bradley"用于原生应用的 OAuth 2.0"BCP 212RFC 8252DOI 10.17487/RFC8252<https://www.rfc-editor.org/info/rfc8252>
[RFC8414]
Jones, M.Sakimura, N.J. Bradley"OAuth 2.0 授权服务器 元数据"RFC 8414DOI 10.17487/RFC8414<https://www.rfc-editor.org/info/rfc8414>
[RFC8705]
Campbell, B.Bradley, J.Sakimura, N.T. Lodderstedt"OAuth 2.0 相互 TLS 客户端身份认证和证书绑定访问 令牌"RFC 8705DOI 10.17487/RFC8705<https://www.rfc-editor.org/info/rfc8705>
[RFC9068]
Bertocci, V."OAuth 2.0 访问令牌的 JSON Web Token (JWT) Profile"RFC 9068DOI 10.17487/RFC9068<https://www.rfc-editor.org/info/rfc9068>

7.2. 资料性参考文献

[arXiv.1508.04324v2]
Mladenov, V.Mainka, C.J. Schwenk"现代单点登录协议的安全性: OpenID Connect 中的二阶漏洞"arXiv:1508.04324v2DOI 10.48550/arXiv.1508.04324<https://arxiv.org/abs/1508.04324v2/>
[arXiv.1601.01229]
Fett, D.Küsters, R.G. Schmitz"OAuth 2.0 的综合形式化安全 分析"arXiv:1601.01229DOI 10.48550/arXiv.1601.01229<https://arxiv.org/abs/1601.01229/>
[arXiv.1704.08539]
Fett, D.Küsters, R.G. Schmitz"Web SSO 标准 OpenID Connect:深入形式化安全分析和安全指南"arXiv:1704.08539DOI 10.48550/arXiv.1704.08539<https://arxiv.org/abs/1704.08539/>
[arXiv.1901.11520]
Fett, D.Hosseyni, P.R. Küsters"OpenID 金融级 API 的广泛形式化安全 分析"arXiv:1901.11520DOI 10.48550/arXiv.1901.11520<https://arxiv.org/abs/1901.11520/>
[bug.chromium]
"使用新标签页打开链接时,Referer 标头包含 URL 片段"Chromium Issue Tracker,Issue ID: 40076763<https://issues.chromium.org/issues/40076763>
[JWT-ENCODED-STATE]
Bradley, J.Lodderstedt, T.H. Zandbelt"使用 JWT 在 OAuth 2 state 参数中编码 claim"进行中的工作Internet-Draft,draft-bradley-oauth-jwt-encoded-state-09<https://datatracker.ietf.org/doc/html/draft-bradley-oauth-jwt-encoded-state-09>
[OAUTH-V2.1]
Hardt, D.Parecki, A.T. Lodderstedt"OAuth 2.1 授权 框架"进行中的工作Internet-Draft,draft-ietf-oauth-v2-1-12<https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12>
[OAuth.Post]
Jones, M.B. Campbell"OAuth 2.0 Form Post Response Mode"OpenID Foundation<https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html>
[OAuth.Responses]
de Medeiros, B., Ed.Scurtescu, M.Tarjan, P.M. Jones"OAuth 2.0 Multiple Response Type Encoding Practices"OpenID Foundation<https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html>
[OpenID.Core]
Sakimura, N.Bradley, J.Jones, M.de Medeiros, B.C. Mortimore"OpenID Connect Core 1.0, 含勘误集 2"OpenID Foundation<https://openid.net/specs/openid-connect-core-1_0.html>
[OpenID.Discovery]
Sakimura, N.Bradley, J.Jones, M.E. Jay"OpenID Connect Discovery 1.0,含勘误集 2"OpenID Foundation<https://openid.net/specs/openid-connect-discovery-1_0.html>
[OpenID.JARM]
Lodderstedt, T.B. Campbell"金融级 API:OAuth 2.0 的 JWT Secured Authorization Response Mode (JARM)"OpenID Foundation<https://openid.net/specs/openid-financial-api-jarm.html>
[owasp.redir]
OWASP Foundation"未验证重定向和 转发速查表"OWASP Cheat Sheet Series<https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html>
[research.cmu]
Chen, E.Pei, Y.Chen, S.Tian, Y.Kotcher, R.P. Tague"面向移动应用开发者的 OAuth 解密"CCS '14:2014 ACM SIGSAC 计算机与 通信安全会议论文集,第 892-903 页DOI 10.1145/2660267.2660323<https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/OAuthDemystified.pdf>
[research.jcs_14]
Bansal, C.Bhargavan, K.Delignat-Lavaud, A.S. Maffeis"通过形式化分析发现网站授权中的具体攻击"Journal of Computer Security,第 22 卷第 4 期,第 601-657 页DOI 10.3233/JCS-140503<https://www.doc.ic.ac.uk/~maffeis/papers/jcs14.pdf>
[research.rub]
Jannett, L.Mladenov, V.Mainka, C.J. Schwenk"DISTINCT:在双窗口单点登录中使用浏览器内通信进行身份盗窃"CCS '22:2022 ACM SIGSAC 计算机 与通信安全会议论文集DOI 10.1145/3548606.3560692<https://dl.acm.org/doi/pdf/10.1145/3548606.3560692>
[research.rub2]
Fries, C."真实 OpenID Connect 实现的安全分析"硕士论文, Ruhr-Universität Bochum (RUB)<https://www.nds.rub.de/media/ei/arbeiten/2021/05/03/masterthesis.pdf>
[research.ubc]
Sun, S.-T.K. Beznosov"魔鬼在(实现)细节中:OAuth SSO 系统的实证分析"2012 ACM 计算机与 通信安全会议 (CCS '12) 论文集,第 378-390 页DOI 10.1145/2382196.2382238<https://css.csail.mit.edu/6.858/2012/readings/oauth-sso.pdf>
[research.udel]
Liu, D.Hao, S.H. Wang"你的所有 DNS 记录都指向我们: 理解悬空 DNS 记录的安全威胁"CCS '16:2016 ACM SIGSAC 计算机与通信安全会议论文集,第 1414-1425 页DOI 10.1145/2976749.2978387<https://dl.acm.org/doi/pdf/10.1145/2976749.2978387>
[RFC2119]
Bradner, S."RFC 中用于 表示要求级别的关键词"BCP 14RFC 2119DOI 10.17487/RFC2119<https://www.rfc-editor.org/info/rfc2119>
[RFC7591]
Richer, J., Ed.Jones, M.Bradley, J.Machulak, M.P. Hunt"OAuth 2.0 动态客户端注册 协议"RFC 7591DOI 10.17487/RFC7591<https://www.rfc-editor.org/info/rfc7591>
[RFC7636]
Sakimura, N., Ed.Bradley, J.N. Agarwal"OAuth 公共客户端的 Proof Key for Code Exchange"RFC 7636DOI 10.17487/RFC7636<https://www.rfc-editor.org/info/rfc7636>
[RFC8174]
Leiba, B."RFC 2119 关键词中大写与 小写的歧义"BCP 14RFC 8174DOI 10.17487/RFC8174<https://www.rfc-editor.org/info/rfc8174>
[RFC8707]
Campbell, B.Bradley, J.H. Tschofenig"OAuth 2.0 的资源指示符"RFC 8707DOI 10.17487/RFC8707<https://www.rfc-editor.org/info/rfc8707>
[RFC9101]
Sakimura, N.Bradley, J.M. Jones"OAuth 2.0 授权 框架:JWT-Secured Authorization Request (JAR)"RFC 9101DOI 10.17487/RFC9101<https://www.rfc-editor.org/info/rfc9101>
[RFC9110]
Fielding, R., Ed.Nottingham, M., Ed.J. Reschke, Ed."HTTP 语义"STD 97RFC 9110DOI 10.17487/RFC9110<https://www.rfc-editor.org/info/rfc9110>
[RFC9126]
Lodderstedt, T.Campbell, B.Sakimura, N.Tonge, D.F. Skokan"OAuth 2.0 Pushed Authorization Requests"RFC 9126DOI 10.17487/RFC9126<https://www.rfc-editor.org/info/rfc9126>
[RFC9207]
Meyer zu Selhausen, K.D. Fett"OAuth 2.0 授权服务器颁发者标识"RFC 9207DOI 10.17487/RFC9207<https://www.rfc-editor.org/info/rfc9207>
[RFC9396]
Lodderstedt, T.Richer, J.B. Campbell"OAuth 2.0 Rich Authorization Requests"RFC 9396DOI 10.17487/RFC9396<https://www.rfc-editor.org/info/rfc9396>
[RFC9440]
Campbell, B.M. Bishop"Client-Cert HTTP 标头字段"RFC 9440DOI 10.17487/RFC9440<https://www.rfc-editor.org/info/rfc9440>
[RFC9449]
Fett, D.Campbell, B.Bradley, J.Lodderstedt, T.Jones, M.D. Waite"OAuth 2.0 Demonstrating Proof of Possession (DPoP)"RFC 9449DOI 10.17487/RFC9449<https://www.rfc-editor.org/info/rfc9449>
[TOKEN-BINDING]
Jones, M.Campbell, B.Bradley, J.W. Denniss"OAuth 2.0 Token Binding"进行中的 工作Internet-Draft, draft-ietf-oauth-token-binding-08<https://datatracker.ietf.org/doc/html/draft-ietf-oauth-token-binding-08>
[W3C.CSP-2]
West, M.Barth, A.D. Veditz"Content Security Policy Level 2"W3C 推荐标准<https://www.w3.org/TR/2016/REC-CSP2-20161215/>最新版本可见 <https://www.w3.org/TR/CSP2/>
[W3C.webappsec-referrer-policy]
Eisinger, J.E. Stark"Referrer Policy"<https://www.w3.org/TR/2017/CR-referrer-policy-20170126/>最新版本可见 <https://www.w3.org/TR/referrer-policy/>
[W3C.WebAuthn]
Hodges, J.Jones, J.C.Jones, M.B.Kumar, A.E. Lundberg"Web Authentication:用于 访问公钥凭据的 API Level 2"W3C 推荐标准<https://www.w3.org/TR/2021/REC-webauthn-2-20210408/>最新版本可见 <https://www.w3.org/TR/webauthn-2/>
[W3C.WebCrypto]
Watson, M., Ed."Web Cryptography API"W3C 推荐标准<https://www.w3.org/TR/2017/REC-WebCryptoAPI-20170126/>最新版本可见 <https://www.w3.org/TR/WebCryptoAPI/>
[WHATWG.CORS]
WHATWG"CORS protocol"Fetch:现行标准,第 3.2 节<https://fetch.spec.whatwg.org/#http-cors-protocol>
[WHATWG.postmessage_api]
WHATWG"跨文档消息传递"HTML:现行标准,第 9.3 节<https://html.spec.whatwg.org/multipage/web-messaging.html#web-messaging>

致谢

我们感谢 Brock AllenAnnabelle Richard BackmanDominick BaierVittorio BertocciBrian CampbellBruno CrispoWilliam DennisGeorge FletcherMatteo GolinelliDick HardtJoseph HeenanPedram HosseyniPhil HuntTommaso InnocentiLouis JannettJared JenningsMichael B. JonesEngin KirdaKonstantin LapineNeil MaddenChristian MainkaJim ManicoNov MatakeDoug McDormanKarsten Meyer zu SelhausenAli MirheidariVladislav MladenovKaan OnariogluAaron PareckiMichael PeckJohan PeetersNat SakimuraGuido SchmitzJörg SchwenkRifaat Shekh-YusefTravis SpencerPetteri SteniusTomek StojeckiDavid WaiteTim WürteleHans Zandbelt 提供的宝贵反馈。

作者地址

Torsten Lodderstedt
SPRIND
John Bradley
Yubico
Andrey Labunets
独立研究员
Daniel Fett
Authlete