| RFC 9700 | OAuth 2.0 安全性 BCP | 2025年1月 |
| Lodderstedt 等 | 最佳当前实践 | [页] |
本文档描述了 OAuth 2.0 当前最佳安全实践。它更新并扩展了 RFC 6749、6750 和 6819 中给出的威胁模型和安全建议,以纳入自 OAuth 2.0 发布以来积累的实践 经验,并涵盖由于 OAuth 2.0 更广泛 应用而产生的新威胁。此外,它还弃用了某些被认为 安全性较低甚至不安全的操作模式。¶
本备忘录记录了一项互联网最佳当前实践。¶
本文档是互联网工程任务组 (IETF)的成果。它代表了 IETF 社区的共识。本文档已经 过公开评审,并已获互联网工程指导组 (IESG)批准发布。有关 BCP 的更多信息 可参见 RFC 7841 第 2 节。¶
有关本文档的当前状态、任何 勘误以及如何提供反馈的信息,可从 https://www.rfc-editor.org/info/rfc9700 获取。¶
Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
自 [RFC6749] 和 [RFC6750] 发布以来,OAuth 2.0(在本文档中简称为 “OAuth”)已在市场上得到广泛采用, 并成为 API 保护的标准以及使用 OpenID Connect [OpenID.Core] 进行联合登录的基础。 虽然 OAuth 用于 各种场景和不同类型的部署,但可以观察到以下 挑战:¶
OAuth 实现正遭受利用已知实现 弱点和反模式(即被认为 不安全的众所周知模式)的攻击。虽然这些威胁中的大多数已在 OAuth 2.0 威胁模型和安全考虑 [RFC6819] 中讨论,但持续发生的利用行为 表明需要更具体的建议、更易于实施的 缓解措施以及更深入的纵深防御。¶
OAuth 正被用于安全要求高于 最初设想的环境,例如开放银行、电子健康、电子政务和 电子签名。这些使用场景需要更严格的指导方针和 额外的保护。¶
OAuth 正被用于比最初预期动态得多的部署中, 从而在安全方面带来了新的挑战。这些挑战超出了 [RFC6749]、[RFC6750] 和 [RFC6819] 的原始范围。¶
OAuth 最初假定客户端、 授权服务器和资源服务器之间存在静态关系。服务器的 URL 在部署时已为客户端所知,并构成了这些参与方之间 信任关系的锚点。验证 客户端是否正在与合法服务器通信,依赖于 TLS 服务器 认证(参见 第 4.5.4 节,见 [RFC6819])。随着 OAuth 得到越来越广泛的采用,这种简单模型逐渐消失,并且在若干 场景中,被一种动态建立关系的方式所取代, 即一方是客户端,另一方是特定部署的授权服务器和资源服务器。 通过这种方式,同一个客户端 可以用于访问不同提供商的服务(对于 标准 API,例如电子邮件或 OpenID Connect),也可以在多租户环境中 充当特定租户的前端。 OAuth 的扩展,例如 OAuth 2.0 动态客户端注册 协议 [RFC7591] 和 OAuth 2.0 授权服务器元数据 [RFC8414],就是为了支持 OAuth 在动态场景中的使用而开发的。¶
技术已经发生变化。例如,浏览器在 重定向请求时处理片段的方式已经改变,隐式许可 所依据的安全模型也随之改变。¶
本文档提供了更新后的安全建议,以应对这些 挑战。它引入了超出现有 规范(例如 OAuth 2.0 [RFC6749] 和 OpenID Connect [OpenID.Core]) 所定义内容的新要求, 并弃用了一些被认为安全性较低甚至 不安全的操作模式。然而,本文档并不取代 [RFC6749]、[RFC6750] 和 [RFC6819] 中给出的安全建议,而是对这些文档进行补充。¶
自然,并非所有现有的生态系统和实现都 与这些新要求兼容,并且遵循本文档中描述的 最佳实践可能会破坏互操作性。尽管如此,仍建议 实现者在可行时尽快升级其实现和生态系统。¶
正在作为 [OAUTH-V2.1] 开发的 OAuth 2.1 将纳入 本文档中的安全建议。¶
本文档的其余部分组织如下:第 2 节 总结了每个 OAuth 实现者最重要的最佳实践。 第 3 节介绍更新后的 OAuth 攻击者 模型。第 4 节对 现实中(截至撰写本文时)发现的威胁和实现问题进行了 详细分析,并讨论了潜在的对策。¶
本文档中的关键词“必须”“不得” “要求”“须”“不得” “应该”“不应该” “建议”“不建议” “可以”和“可选”,仅当它们像 此处所示 以全大写形式出现时,才应按照 BCP 14 [RFC2119] [RFC8174] 中所述的含义 进行解释。¶
本规范使用 OAuth 2.0 [RFC6749] 定义的术语“访问令牌”“授权 端点”“授权许可”“授权服务器”“客户端” “客户端标识符”(客户端 ID)“受保护资源”“刷新 令牌”“资源所有者”“资源服务器”和“令牌端点”。¶
“开放重定向器”是 Web 服务器上的一个端点,它将用户的 浏览器转发到从查询参数中获取的任意 URI。¶
本节描述了截至撰写本文时被认为属于 最佳实践的核心安全机制和措施。关于这些 安全机制和措施的详细信息(包括详细的攻击 描述),以及较少使用的选项的要求,可参见 第 4 节。¶
将客户端重定向 URI 与预注册 URI 进行比较时,
授权
服务器必须使用精确字符串匹配,但原生应用的
localhost 重定向 URI 中的端口号除外(参见 第 4.1.3 节)。此
措施有助于防止授权码和
访问令牌泄露(参见 第
4.1 节)。它还可以帮助检测
混淆攻击(参见 第 4.4 节)。¶
客户端和授权服务器不得暴露 将用户浏览器转发到 从查询参数获取的任意 URI 的 URL(开放重定向器),如 第 4.11 节所述。开放 重定向器可能导致授权码和访问令牌 被窃取。¶
客户端必须防止跨站请求伪造
(CSRF)。在此
上下文中,CSRF 指向重定向端点发出的请求并非
源自授权服务器,而是源自恶意第三方
(详情参见 第
4.4.1.8 节,见 [RFC6819])。
已确认
授权服务器支持代码交换证明密钥(PKCE)[RFC7636] 的客户端可以
依赖 PKCE 提供的 CSRF 保护。在 OpenID Connect 流程中,
nonce 参数提供 CSRF 保护。否则,必须使用
在 state 参数中携带并与用户代理安全
绑定的一次性 CSRF 令牌来提供 CSRF 保护(参见
第 4.7.1 节)。¶
当 OAuth 客户端可以与多个授权服务器交互时, 要求针对混淆攻击采取防御措施(参见 第 4.4 节)。为此,客户端 应该¶
iss
参数作为对策;或者¶
iss 值的替代对策
(例如 [OpenID.Core] 中 ID Token 的
iss claim
或 [OpenID.JARM] 响应中的
iss 值),并按照
[RFC9207] 中所述处理
该值。¶
在没有这些选项的情况下,客户端可以 改为使用不同的重定向 URI 来标识授权端点和令牌端点,如 第 4.4.2 节所述。¶
重定向可能包含用户凭据的请求的授权服务器 必须避免意外转发这些用户凭据(详情参见 第 4.12 节)。¶
客户端必须防止授权码 注入攻击(参见 第 4.5 节),并使用以下选项之一防止授权码被滥用:¶
nonce 参数以及 ID Token 中
对应的 Claim。¶
无论如何,PKCE challenge 或 OpenID Connect nonce
必须
针对具体事务,并与启动该事务的客户端和用户代理
安全绑定。
鼓励授权服务器作出合理努力,以检测并
防止对 PKCE challenge 或 OpenID Connect
nonce 使用固定值。¶
注意:虽然 PKCE 被设计为保护原生 应用的机制,但此建议适用于所有类型的 OAuth 客户端,包括 Web 应用。¶
使用 PKCE 时,客户端应该使用
不会在授权请求中暴露 PKCE verifier 的 PKCE
code challenge 方法。
否则,能够读取授权请求的攻击者(参见 第 3 节中的攻击者 (A4))便可以破坏 PKCE 提供的安全性。
目前,S256 是唯一这样的方法。¶
如果客户端在
授权请求中发送有效的 PKCE code_challenge 参数,则授权服务器必须在令牌端点
强制正确使用
code_verifier。¶
授权服务器必须通过确保
仅当授权请求中存在
code_challenge 参数时,才接受包含 code_verifier 参数的令牌请求,
从而缓解 PKCE 降级攻击;详情参见
第 4.8.2 节。¶
授权服务器必须提供一种方式
来检测其是否支持 PKCE。建议授权服务器在其授权服务器元数据
[RFC8414] 中发布
code_challenge_methods_supported 元素,
其中包含受支持的 PKCE challenge 方法(客户端可以使用这些方法
检测是否支持 PKCE)。授权服务器可以
改为提供一种
特定于部署的方式,以确保或确定授权服务器是否支持 PKCE。¶
隐式许可(响应类型 token)以及其他
会导致授权服务器在
授权响应中签发访问令牌的响应类型,容易遭受
第 4.1、4.2、4.3 和
4.6 节所述的访问令牌泄露和
访问令牌重放攻击。¶
此外,当访问令牌在 授权响应中签发时,不存在将访问令牌绑定到特定客户端的标准化 发送者约束方法(如 第 2.2 节所建议)。 这意味着攻击者可以在资源端点使用泄露或被盗的 访问令牌。¶
为避免这些问题,客户端不应该使用隐式
许可(响应类型 token)或其他在授权响应中签发
访问令牌的响应类型,除非能够防止授权响应中的访问令牌
注入,并缓解上述令牌泄露
途径。¶
客户端应该改为使用响应
类型 code(即授权
码许可类型),如 第
2.1.1 节所规定,或者使用任何其他
会导致授权服务器在令牌
响应中签发访问令牌的响应类型,例如 code id_token 响应类型。这使
授权服务器能够检测攻击者的重放尝试,并且
通常会减少攻击面,因为访问令牌不会
暴露在 URL 中。它还使授权服务器能够
对所签发的令牌实施发送者约束(参见 第 2.2
节)。¶
与访问令牌关联的权限应该限制为 特定应用或使用场景所需的最低限度。这样可以 防止客户端超出资源所有者授权的权限。 它还可以防止用户超出相应安全策略 授权给他们的权限。权限限制 还有助于降低访问令牌泄露造成的影响。¶
具体而言,访问令牌应该
将受众限制为特定资源
服务器,或者如果无法做到这一点,则限制为少量资源服务器。为实现这一点,
授权服务器将
访问令牌与特定资源服务器关联,并且每个资源
服务器都有义务对每个请求验证,该请求中发送的访问
令牌是否旨在供该特定
资源服务器使用。如果不是,资源服务器必须拒绝处理
相应请求。[RFC9068] 中定义的 aud
claim可以
用于对访问令牌实施受众限制。客户端和授权服务器可以分别使用
[RFC6749] 和
[RFC8707] 中规定的
scope 或 resource 参数,确定它们希望访问的
资源服务器。¶
此外,访问令牌应该限制为
资源服务器上的特定资源
和操作。为实现这一点,
授权服务器将访问令牌与
相应资源和操作关联,并且每个资源服务器都有义务
对每个请求验证,该请求中发送的访问令牌
是否旨在用于对该
特定资源执行该特定操作。如果不是,资源服务器必须拒绝处理
相应请求。客户端和授权服务器可以使用
[RFC6749] 中规定的 scope 参数和
[RFC9396] 中规定的
authorization_details,
确定这些
资源和/或操作。¶
[RFC6749] 中的资源所有者密码凭据许可不得 使用。此许可类型会以不安全的方式将资源 所有者的凭据暴露给客户端。即使客户端是善意的,使用这种许可也会增加 攻击面(即凭据可能在授权服务器之外的更多位置泄露), 并会使用户养成在授权 服务器之外的位置输入其凭据的习惯。¶
此外,资源所有者密码凭据许可并非设计用于 双因素认证以及需要 多个用户交互步骤的认证过程。使用加密凭据进行认证 (参见 WebCrypto [W3C.WebCrypto]、 WebAuthn [W3C.WebAuthn])可能 无法使用这种许可类型实现,因为它通常绑定到特定 Web 源。¶
如果在 特定部署中可以建立向客户端签发/注册 凭据并确保这些凭据机密性的流程,则授权服务器应该强制执行客户端 认证。¶
建议使用非对称加密进行
客户端认证,例如 OAuth 2.0 的双向 TLS [RFC8705] 或依据
[RFC7521] 和 [RFC7523] 的签名 JWT
(“私钥 JWT”)。后者在 [OpenID.Core] 中定义为客户端
认证方法 private_key_jwt)。
当客户端认证使用非对称加密时,授权
服务器无需存储敏感的对称密钥,因此这些
方法对密钥泄露具有更强的抵抗能力。¶
使用 OAuth 授权服务器元数据 [RFC8414] 有助于提高 OAuth 部署的安全性:¶
因此,建议授权 服务器按照 [RFC8414] 发布 OAuth 授权服务器元数据,并且客户端使用 此授权服务器元数据(如果可用)来配置自身。¶
在 第
4.15.1 节所述条件下,
授权服务器不应该允许客户端影响其
client_id 或
任何其他可能导致其与真正资源所有者产生混淆的 claim。¶
建议客户端与 资源服务器之间按照 [BCP195] 使用端到端 TLS。如果 TLS 流量需要在中间设备处终止,请参阅 第 4.13 节了解进一步的安全 建议。¶
授权响应不得通过未加密的网络
连接传输。为此,授权服务器不得允许
使用 http
方案的重定向 URI,但按照
第 7.3 节,见 [RFC8252] 所述使用环回接口重定向的原生客户端除外。¶
如果授权响应使用类似 postMessage [WHATWG.postmessage_api] 的浏览器内通信技术 而不是 HTTP 重定向发送,则浏览器内消息的 发起方和接收方必须按照 第 4.17 节所述进行严格 验证。¶
为了支持基于浏览器的客户端,由此类客户端直接访问的端点,
包括令牌端点、授权服务器元数据端点、jwks_uri
端点和动态客户端注册端点,可以支持使用
跨源资源共享(CORS)[WHATWG.CORS]。
但是,授权端点不得
支持 CORS,因为客户端并不直接访问此
端点;相反,客户端会将用户代理重定向到该端点。¶
[RFC6819] 给出了一个威胁模型, 描述了 OAuth 部署 必须防范的威胁。在此过程中,[RFC6819] 对 攻击者及其能力作出了一些假设,即隐式地 建立了一个攻击者模型。下面将明确说明这一攻击者模型, 并对其进行更新和扩展,以考虑涉及多个参与方的潜在动态 关系(如 第 1 节所述), 纳入新的攻击者类型,并更清晰地定义攻击者模型。¶
本文档的目标是确保资源 所有者(使用用户代理)在授权服务器上的授权,以及随后在 资源服务器上对访问令牌的使用,尽实际 可能得到保护,至少能够防御以下攻击者。¶
Web 攻击者可以建立和运行任意数量的 网络端点(除“诚实”端点外),包括浏览器和 服务器。Web 攻击者可以建立资源 所有者会访问的网站,运行自己的用户代理,并参与协议。¶
特别是,Web 攻击者可以运行已在 授权服务器注册的 OAuth 客户端,也可以运行自己的授权服务器和 资源服务器,而资源所有者和其他资源所有者可以 使用这些服务器(与“诚实”服务器并行)。¶
还必须假定 Web 攻击者可以诱骗用户在 任何时候让其浏览器导航到攻击者任意选择的 URI。在实践中,这 可以通过许多方式实现,例如将恶意 广告注入广告网络,或者发送 看似合法的电子邮件。¶
Web 攻击者可以使用自己的用户凭据创建新的 消息,也可以使用此前获知的任何秘密。例如, 如果 Web 攻击者通过配置错误的重定向 URI 得知了某个用户的授权码, 该 Web 攻击者随后可以 尝试用该授权码兑换访问令牌。¶
但是,他们无法读取或操纵并非 发给他们的消息(例如,发送给不受攻击者控制的 授权服务器 URL 的消息)。¶
网络攻击者还完全控制 协议参与者用于通信的网络。他们可以 窃听、操纵和伪造消息,但这些消息 受到加密方法(例如 TLS)正确保护时除外。 网络攻击者还可以阻止任意消息。¶
例如,Web 攻击者可以是互联网 服务提供商的客户,而网络攻击者可以是互联网服务 提供商本身、在公共(Wi-Fi)网络中使用 ARP 欺骗的攻击者,或者能够访问互联网 交换点的国家支持攻击者。¶
上述攻击者 (A1) 和 (A2) 符合 OAuth 正式分析 工作 [arXiv.1601.01229] 所使用的攻击者模型。 这是一个最小攻击者模型。 实现者必须考虑其 OAuth 实现 所处环境中所有可能类型的攻击者。例如,在 [arXiv.1901.11520] 中, 使用了一个非常强的攻击者模型,其中包括完全 控制令牌端点的攻击者。这模拟了生态系统中 端点可能配置错误所产生的影响,而通过按照 第 2.6 节所述使用授权服务器元数据,可以避免这种情况。因此,此类攻击者未列在此处。¶
然而,以往针对 OAuth 的攻击表明,以下类型的 攻击者尤其值得关注:¶
能够读取但无法修改 授权请求内容的攻击者(即授权请求可能 以与上述相同的方式泄露给攻击者)。¶
(A3)、(A4) 和 (A5) 通常会与 (A1) 或 (A2) 同时出现。 攻击者可以相互协作以实现共同目标。¶
请注意,攻击者 (A1) 或 (A2) 本身可以是资源所有者,或者 充当资源所有者。例如,此类攻击者可以使用自己的浏览器,在客户端或资源服务器上 重放通过上述任一种攻击获得的 令牌或授权码。¶
本节详细描述了针对 OAuth 实现的攻击, 以及潜在的对策。已经在 [RFC6819] 中涵盖的攻击和缓解措施不再列于此处,除非提出了新的 建议。¶
本节还针对某些情况和协议 选项定义了额外要求(超出 第 2 节中定义的要求)。¶
一些授权服务器允许客户端注册重定向 URI 模式,而不是完整的重定向 URI。然后,授权服务器 会在运行时将授权 端点处的重定向 URI 参数值与已注册的模式进行匹配。这种方法 允许客户端将事务状态编码到额外的重定向 URI 参数中,或者为多个 重定向 URI 注册单一模式。¶
事实证明,与精确的重定向 URI 匹配相比,这种方法实现起来更复杂, 管理起来也更容易出错。现实中已经观察到多起 利用模式匹配 实现或具体配置缺陷的成功攻击 (例如,参见 [research.rub2])。 对重定向 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 向授权端点发起以下授权请求(换行 仅用于显示):¶
授权服务器验证重定向 URI,并将
其与客户端 s6BhdRkqt3 已注册的重定向 URL 模式进行比较。
授权请求随后被处理并呈现给用户。¶
如果用户没有看到重定向 URI,或者没有识别出 攻击,则授权码会被签发并立即发送到攻击者的 域。如果启用了授权自动批准 (根据 [RFC6749],不建议公共客户端这样做),则甚至可以在无需用户 交互的情况下实施攻击。¶
如果攻击者冒充公共客户端,则攻击者可以 在相应的令牌端点将该授权码兑换为令牌。¶
对于机密客户端,这种攻击不会那么容易奏效,因为 授权码兑换需要使用合法客户端的 密钥进行认证。但是,攻击者可以通过执行授权码 注入攻击,使用合法的机密 客户端兑换授权码;参见 第 4.5 节。¶
需要特别指出的是,即使授权
服务器正确处理通配符,也可能存在重定向 URI 验证
漏洞。例如,假设客户端
注册了重定向 URL 模式 https://*.somesite.example/*,并且
授权服务器将其解释为“允许重定向 URI
指向 somesite.example 域中的任何主机”。如果
攻击者成功在
somesite.example 中建立了一个主机或子域,则攻击者可以冒充合法客户端。例如,这
可能由子域接管攻击 [research.udel]
引起,其中
一个过期的 CNAME 记录(例如 external-service.somesite.example)
指向一个已不存在的外部 DNS 名称(例如
customer-abc.service.example),而攻击者可以接管它
(例如,通过在外部服务中注册为 customer-abc)。¶
上述攻击同样适用于隐式许可。如果 攻击者能够将授权响应发送到攻击者控制的 URI, 攻击者将直接获得携带 访问令牌的片段。¶
此外,隐式许可(以及使用
[OAuth.Responses] 中定义的
response_mode=fragment 时的其他许可)还可能受到另一类
攻击。该攻击利用了这样一个事实:如果 Location 标头
不包含片段,则用户代理会把片段重新附加到
重定向的目标 URL(参见 第 17.11 节,见 [RFC9110])。这里描述的攻击
将这种行为与客户端充当开放
重定向器(参见 第
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。¶
随后,该网站发起一个授权请求,该请求
与针对授权码流程的攻击中的请求非常相似。与
上面不同的是,它通过将
redirect_to=https://attacker.example 编码到
重定向 URI 的参数中来利用开放重定向器,并使用响应类型 token(换行仅用于
显示):¶
然后,由于重定向 URI 与已注册模式匹配, 授权服务器允许该请求,并在 303 重定向中发送生成的访问 令牌(为提高可读性,省略了一些响应参数):¶
在 client.somesite.example, 请求到达开放
重定向器。该端点将
读取重定向参数,并向 URL https://attacker.example/ 发出 HTTP 303 Location 标头
重定向。¶
由于 client.somesite.example 上的重定向器没有在
Location 标头中包含片段,因此用户代理会将
原始片段 #access_token=2YotnFZFEjr1zCsicMWpAA&... 重新附加到
URL,并导航到以下 URL:¶
https://attacker.example/#access_token=2YotnFZFEjr1z...¶
攻击者位于 attacker.example 的页面随后可以
访问该
片段并获得访问令牌。¶
正确实现和管理模式匹配的复杂性显然
会引发安全问题。因此,本文档建议通过使用精确的重定向 URI 匹配来简化所需的
逻辑和配置。这意味着
授权服务器必须确保两个 URI 相等;
详情参见 第
6.2.1 节,见 [RFC3986]
中的“简单字符串比较”。
唯一的例外是
使用 localhost URI 的原生应用:在这种情况下,授权服务器
必须按照 第
7.3 节,见 [RFC8252] 所述允许可变
端口号。¶
其他建议:¶
#_),阻止浏览器重新附加
片段。¶
如果能够验证包含 重定向 URI 的授权请求的来源和完整性,例如使用 [RFC9101] 或结合客户端 认证使用 [RFC9126] 时,授权服务器可以信任 重定向 URI, 而无需进一步检查。¶
授权请求 URI 或授权
响应 URI 的内容可能分别从授权
服务器的网站或客户端的网站中,经由
Referer HTTP 标头(参见 第 10.1.3 节,见 [RFC9110])无意泄露给攻击者。最重要的是,
授权码或 state 值可能以这种方式泄露。虽然
第
10.1.3 节,见 [RFC9110]
中另有规定,
由于浏览器实现问题,通过 URI 片段传递的访问令牌也可能出现
同样的情况,Chromium 项目中的一个(现已修复的)问题
[bug.chromium] 就说明了这一点。¶
从 OAuth 客户端发生泄露要求客户端在 授权请求成功后呈现一个页面,该页面¶
一旦浏览器导航到攻击者的页面或加载了
第三方内容,攻击者就会收到授权响应
URL,并可从中提取 code 或 state(以及可能的
access_token)。¶
攻击者如果通过
Referer 标头获知有效授权码或访问令牌,就可以执行第
4.1.1、4.5 和
4.6 节所述的攻击。如果攻击者
获知 state,则通过 state 获得的 CSRF
保护将失效,从而导致
第
4.4.1.8 节,见 [RFC6819] 所述的
CSRF
攻击。¶
作为 OAuth 授权响应结果而呈现的页面以及 授权端点不应该包含第三方 资源或指向外部站点的 链接。¶
以下措施可进一步降低攻击成功的 可能性:¶
Referrer-Policy:
no-referrer 会完全禁止由生成的文档发出的所有请求中的 Referer
标头。¶
如 第 4.1.2 节,见 [RFC6749] 所述,授权码 在令牌 端点首次使用后,授权服务器必须使其失效。例如,如果授权服务器在 合法客户端兑换授权码后使其失效,攻击者随后将无法再兑换 此授权码。¶
如果攻击者设法在合法客户端之前 将授权码兑换成令牌,则这一措施无法缓解攻击。 因此,[RFC6749] 进一步建议,当 尝试重复兑换某个授权码时,授权服务器应该撤销此前基于该授权码签发的所有 令牌。¶
state 值在重定向端点
首次使用后,客户端应该使其失效。如果实现了这一点,并且
攻击者通过客户端网站的 Referer 标头收到令牌,则
state 已被使用并由
客户端使其失效,因此攻击者无法再次使用它。(如果 state 从
授权服务器的网站泄露,这种措施无效,因为此时 state
尚未在客户端的重定向端点使用。)¶
对授权响应使用 form post 响应模式,而不是重定向 (参见 [OAuth.Post])。¶
授权码和访问令牌可能最终出现在浏览器的 已访问 URL 历史记录中,从而导致下文所述的 攻击。¶
如果已经拥有令牌的客户端或
网站故意导航到类似
provider.com/get_user_profile?access_token=abcdef
的页面,则访问令牌可能最终进入浏览器历史记录。[RFC6750]
不鼓励这种做法,并建议通过标头传输令牌,
但在实践中,网站经常通过查询
参数传递访问令牌。¶
在隐式许可的情况下,类似
client.example/redirection_endpoint#access_token=abcdef 的 URL 也可能
因提供商授权端点的重定向而最终
进入浏览器历史记录。¶
对策:¶
当 OAuth 客户端与 两个或更多授权服务器交互,并且至少一个授权 服务器受攻击者控制时,可能发生混淆攻击。例如, 如果攻击者使用动态注册将 客户端注册到其自己的授权服务器,或者某个授权服务器 遭到入侵,就可能出现这种情况。¶
攻击的目标是获得未受入侵的授权服务器所签发的授权码或访问 令牌。其实现方式是 诱骗客户端将这些凭据发送给受入侵的 授权服务器(攻击者),而不是在 未受入侵的授权/资源 服务器的相应端点使用这些凭据。¶
这里的描述遵循 [arXiv.1601.01229],并在 下文概述了攻击的不同变体。¶
前提条件:为了使此攻击变体奏效,假设¶
下面进一步假设客户端已
在 H-AS(URI:
https://honest.as.example,客户端 ID:7ZGZldHQ)和 A-AS
(URI:
https://attacker.example,客户端 ID:666RVZJTA)上注册。下面
示例中显示的 URL 为便于展示而进行了缩短,仅包含与
攻击相关的参数。¶
针对授权码许可的攻击:¶
https://attacker.example/authorize?response_type=code&client_id=666RVZJTA。¶
https://honest.as.example/authorize?response_type=code&client_id=7ZGZldHQ¶
用户授权客户端访问其 在 H-AS 上的资源。(请注意, 警觉的用户可能会在此时发现其原本打算使用 A-AS 而不是 H-AS。下面列出的第一个攻击变体不存在此 限制。)H-AS 签发授权码,并通过浏览器将其发送回客户端。¶
由于客户端仍然认为该授权码是由 A-AS 签发的, 因此会尝试在 A-AS 的令牌端点兑换该授权码。¶
因此攻击者获得了授权码,并且可以 将该授权码兑换为访问令牌(对于公共客户端),或者执行 第 4.5 节所述的 授权码注入攻击。¶
变体:¶
当 OAuth 客户端只能与一个授权 服务器交互时,不需要针对混淆攻击采取防御措施。但是,在 OAuth 客户端与两个 或更多授权服务器交互的场景中,客户端必须防止 混淆攻击。下面讨论两种 不同的方法。¶
对于这两种防御措施,客户端必须为每个授权请求 存储其发送授权请求的 发行者,并将此信息绑定到 用户代理。通过关联的元数据,发行者可作为授权端点和令牌端点组合的抽象 标识符, 该组合将在流程中使用。如果无法获得发行者标识符(例如, 既未使用 OAuth 授权服务器元数据 [RFC8414],也未使用 OpenID Connect Discovery [OpenID.Discovery]),则可以 改用该元组的其他唯一标识符,或者直接使用该元组本身。 为简化表述,下文将这种特定于部署的标识符 统一归入发行者(或发行者标识符)的概念。¶
需要注意的是,仅存储授权服务器 URL 不足以识别 混淆攻击。攻击者可能将未受入侵的授权服务器的 授权端点 URL 声明为 “自己的”授权服务器 URL,却声明一个由其自身 控制的令牌端点。¶
此防御要求授权服务器在授权响应中向客户端发送 其发行者标识符。 在收到授权 响应时,客户端必须将收到的发行者 标识符与已存储的 发行者标识符进行比较。如果不匹配,客户端必须中止 交互。¶
可以通过不同方式将该发行者标识符 传输给客户端:¶
iss
传输发行者信息。¶
iss claim。¶
在这两种情况下,iss 值必须按照
[RFC9207] 进行评估。¶
虽然这种防御可能需要部署新的 OAuth 功能来传输 发行者信息,但它是一种稳健且相对简单的混淆 攻击防御措施。¶
对于这种防御,客户端必须 为其交互的每个发行者使用不同的重定向 URI。¶
客户端必须通过将 发行者对应的不同重定向 URI 与实际接收 授权响应的 URI 进行比较,检查授权响应是否来自正确的 发行者。如果不匹配,客户端 必须中止该流程。¶
虽然这种防御基于现有 OAuth 功能,但在客户端仅注册一次即可使用多个不同 发行者的场景中(例如某些开放银行方案)无法使用;而且由于与 客户端注册紧密集成,它更难自动部署。¶
此外,攻击者可能能够绕过 此防御所提供的保护,方法是向“诚实的”授权服务器注册一个新客户端, 并使用客户端分配给攻击者授权服务器的重定向 URI。随后攻击者便可以按上述方式实施 攻击,将 客户端 ID 替换为其新创建客户端的客户端 ID。¶
因此,只有在其他选项不可用时才应该 使用这种防御。¶
获得授权响应中所含授权码的攻击者 (参见 第 3 节中的 攻击者 (A3))可以尝试将该 授权码兑换为访问令牌,或者以其他方式使用该 授权码。¶
如果授权码是为公共客户端创建的, 攻击者可以将授权码发送到 授权服务器的令牌端点,从而获得访问令牌。此攻击在 第 4.4.1.1 节,见 [RFC6819] 中已有描述。¶
对于机密客户端,或者在某些特殊情况下,攻击者可以 执行授权码注入攻击,如下所述。¶
在授权码注入攻击中,攻击者试图将 被盗的授权码注入攻击者自己与客户端的会话中。其 目的是将攻击者在客户端处的会话与受害者的 资源或身份关联起来,从而至少让攻击者获得对 受害者资源的有限访问权限。¶
除了绕过机密客户端的客户端认证之外,此 攻击的其他使用场景包括:¶
除这些特殊情况外,当授权码是为公共客户端创建时,授权码注入通常并无 意义,因为如上所述,直接将授权码 发送到令牌端点是一种更简单且威力更大的攻击。¶
授权码注入攻击按如下方式进行:¶
redirect_uri、客户端的客户端 ID 和
客户端密钥(或其他客户端认证方式)。¶
显然,如果授权码是签发给 另一个客户端 ID 的,例如攻击者建立的客户端,则检查步骤(步骤 5)会失败。如果授权码已经被 合法用户兑换并且只能使用一次,检查 同样会失败。¶
如果授权服务器存储了授权请求中使用的
完整重定向 URI,并将其与 redirect_uri 参数进行比较,
那么通过被篡改的重定向 URI 获得的授权
码注入尝试也应该会被检测到。¶
第 4.1.3 节,见 [RFC6749] 要求授权服务器¶
确保如果初始授权 请求中按照第 4.1.1 节所述包含了 “redirect_uri”参数,则“redirect_uri”参数必须存在, 并且如果包含,则确保 两者的值完全相同。¶
在 第 4.5.1 节所述的攻击场景中,合法 客户端会使用其在 授权请求中始终使用的正确重定向 URI。但是,该 URI 不会与攻击者使用的被篡改 重定向 URI 匹配(否则重定向不会 落到攻击者的页面上)。因此,授权服务器会检测到 攻击,并拒绝兑换授权码。¶
在满足某些条件时,此检查还可以检测从另一设备上的 同一客户端的另一个实例获得的授权 码注入尝试:¶
但是,这种方法与在授权端点强制执行精确
重定向
URI 匹配的理念相冲突。此外,实际观察发现,提供商在此阶段经常忽略 redirect_uri 检查
要求,
可能是因为从规范文字来看,这一要求似乎并非
安全关键。¶
其他提供商只是将 redirect_uri
参数
与已注册的重定向 URI 模式进行模式匹配。这使
授权服务器无需为每个事务存储实际重定向
URI 与相应授权码之间的关联。但是,
这种检查显然没有实现
规范的意图,因为并未考虑被篡改的重定向 URI。因此,
任何试图注入使用合法客户端
client_id 获得的授权码,或通过在另一设备上使用合法
客户端而获得的授权码的行为,在相应
部署中都不会被检测到。¶
还假定 第 4.1.3 节,见 [RFC6749] 中定义的要求会增加 客户端实现复杂度,因为客户端 需要存储或重建调用 令牌端点所需的正确重定向 URI。¶
客户端认证的非对称方法无法阻止此 攻击,因为 是合法客户端在令牌端点进行认证。¶
因此,本文档建议改为使用下文所述的某种 机制,在特定事务的上下文中,将每个 授权 码绑定到特定设备上的某个客户端实例(或某个 用户代理中)。¶
有两种很好的技术方案可将授权 码绑定到客户端 实例,如下所述。¶
[RFC7636] 中规定的 PKCE 机制可用作对策
(尽管它最初被设计用于保护原生应用)。当
攻击者试图注入授权码时,对
code_verifier 的检查会失败:客户端使用其正确的 verifier,但该
授权码
与一个不匹配此
verifier 的 code_challenge 关联。¶
PKCE 不仅能防止授权码
注入攻击,
还可以保护为公共客户端创建的授权码:PKCE 确保
攻击者在不知道 code_verifier 的情况下,无法在
授权服务器的令牌端点兑换被盗的授权码。¶
OpenID Connect 现有的 nonce 参数可以
防止授权
码注入攻击。nonce 值只能使用一次,并由
客户端创建。客户端应将其绑定到用户代理会话,并在
初始请求中将其发送给 OpenID Provider(OP)。OP 将收到的
nonce 值放入 ID Token 中,该 ID Token 作为在令牌端点进行授权码兑换的一部分
签发。
如果攻击者在授权响应中注入
授权码,则客户端
会话中的 nonce 值与从令牌
端点收到的 ID Token 中的 nonce 值将不匹配,从而
检测到攻击。这里假定攻击者无法获取受害者设备上的用户代理
状态(攻击者正是从该设备中窃取了相应的
授权
码)。¶
需要注意的是,只有在客户端
正确检查从令牌端点获得的 ID Token 中的 nonce 参数,并且在此检查成功之前不使用任何
已签发令牌的情况下,此对策才有效。更准确地说,使用 nonce 参数防止
授权码注入的客户端¶
nonce,
即使已经从授权响应中获得了另一个 ID Token
(例如 response_type=code+id_token);并且¶
需要注意的是,nonce 无法
保护公共客户端的授权码,因为攻击者不需要执行授权码
注入攻击。相反,攻击者可以直接使用
被盗的授权码调用令牌端点。¶
如果攻击者能够修改受害者授权请求中使用的
nonce 或 code_challenge 值,则攻击者可以绕过上述对策。
攻击者可以将这些值
修改为与客户端在攻击者自己的会话中所选择的值相同,
即上述攻击 步骤 2 中的值。(这要求受害者
与客户端的会话在攻击者已经启动其
与客户端的会话之后才开始。)如果攻击者随后能够捕获
受害者的授权码,那么即使使用 PKCE 或 nonce,攻击者仍然能够在
步骤
3 中注入被盗授权码。¶
这种攻击很复杂,并且需要攻击者与 受害者会话之间进行紧密交互。尽管如此,仍然需要采取措施阻止 攻击者读取授权响应的内容, 如第 4.1、4.2、 4.3、4.4 和 4.11 节所述。¶
在访问令牌注入攻击中,攻击者试图将 被盗的访问令牌注入合法客户端(该客户端不受 攻击者控制)。通常,当攻击者希望 利用泄露的访问令牌在某个特定 客户端中冒充用户时,会发生这种攻击。¶
为实施攻击,攻击者使用隐式许可与
客户端启动 OAuth 流程,并通过替换授权
服务器签发的访问令牌来修改授权
响应,或者直接伪造一个包含
泄露访问令牌的授权服务器响应。由于响应包含
客户端为该特定事务生成的 state 值,因此客户端
不会将响应视为 CSRF 攻击,并使用攻击者
注入的访问令牌。¶
在纯 OAuth 流程中,没有办法检测此类注入攻击,因为令牌在签发时没有与 事务或特定用户代理进行任何绑定。¶
在 OpenID Connect 中,可以缓解该攻击,因为授权
响应
还包含一个带有 at_hash claim 的 ID Token。因此,攻击者
必须同时替换响应中的访问令牌和 ID Token。
攻击者无法伪造 ID Token,因为它已签名或使用认证
加密。攻击者也无法注入与
被盗访问令牌匹配的泄露 ID Token,因为泄露的 ID Token 中的 nonce claim
极有可能包含与授权响应中
预期值不同的值。¶
请注意,仍然需要进一步的保护,例如发送者约束访问令牌, 以防止攻击者直接在资源 端点使用访问令牌。¶
攻击者可能试图向受害者设备上 合法客户端的重定向 URI 注入请求,例如使 客户端访问受攻击者控制的资源。这是 一种称为跨站请求伪造(CSRF)的攻击变体。¶
一种长期使用的对策是客户端在 state 参数中传递一个随机
值,也
称为 CSRF Token,该值将发往重定向 URI 的请求与用户代理会话关联起来,如前所述。此
对策在 第
5.3.5 节,见 [RFC6819] 中有详细描述。
PKCE 或 OpenID Connect 的 nonce 值也提供
相同的保护。¶
使用 PKCE 代替 state 或 nonce
来提供 CSRF 保护时,
需要注意:¶
客户端必须在使用 PKCE 进行
CSRF 保护之前,确保
授权服务器支持 PKCE。如果授权服务器不支持 PKCE,
则必须使用 state 或 nonce
提供 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 攻击。¶
支持 PKCE 但没有强制要求 所有流程都使用 PKCE 的授权服务器可能容易受到 PKCE 降级攻击。¶
此攻击的第一个前提条件是授权请求中存在一个
可由攻击者控制的
标志,用于为特定
流程启用或禁用 PKCE。code_challenge 参数是否存在正适合
用于此目的,即:如果该
参数存在于授权请求中,授权服务器便启用并强制执行 PKCE;而如果
缺少该参数,则不强制执行 PKCE。¶
此攻击的第二个前提条件是客户端完全没有使用
state
(例如,因为客户端依赖 PKCE 防止 CSRF),或者
客户端没有正确检查 state。¶
概括来说,此攻击是 CSRF 攻击的一种变体。攻击者 实现了与 第 4.7 节所述攻击相同的目标:攻击者将一个 绑定到攻击者资源的授权码(以及由此得到的访问令牌)注入其受害者与客户端之间的 会话中。¶
code_challenge=hash(abc) 设置为 PKCE code challenge(散列
函数和参数编码按照 [RFC7636]
中的定义)。客户端现在
正在等待从用户浏览器接收授权响应。¶
code_challenge=hash(xyz)。攻击者拦截该请求,并从请求中删除整个
code_challenge 参数。由于此步骤是在
攻击者的设备上完成的,因此攻击者可以完全访问请求内容,
例如使用浏览器调试工具。¶
code_verifier=abc 作为 PKCE code verifier。¶
code_verifier 参数是否存在或其内容。它将向用户控制下的客户端签发一个访问令牌
(该令牌属于
攻击者的资源)。¶
正确使用 state 可以防止此攻击。
但是实践表明,
许多 OAuth 客户端没有使用或没有正确检查 state。¶
因此,授权服务器必须 缓解此攻击。¶
请注意,从授权服务器的角度来看,在上述
攻击中,
令牌端点会收到一个 code_verifier 参数,尽管
签发该授权码的 OAuth
流程的授权请求中并不存在
code_challenge 参数。¶
code_challenge,
则令牌请求中必须存在
有效的 code_verifier。¶
除此之外,为防止 PKCE 降级攻击,授权
服务器必须确保:
如果授权请求中不存在 code_challenge,则必须拒绝
包含 code_verifier 的令牌端点请求。¶
强制要求使用 PKCE(无论是普遍要求还是 针对特定客户端)的授权服务器 已隐式实现了此安全措施。¶
在某些 情况下,访问令牌可能从资源服务器泄露。¶
攻击者可能建立自己的资源服务器,并诱骗客户端 向其 发送对其他资源服务器有效的访问令牌 (参见 第 3 节中的攻击者 (A1) 和 (A5))。如果客户端将有效访问令牌发送给 这个伪造的资源服务器,则攻击者随后可能使用该 令牌代表资源所有者访问其他服务。¶
此攻击假定客户端在开发时没有绑定到某一个特定资源 服务器(及其 URL),而是客户端实例在运行时 获得资源服务器 URL。 这种后期 绑定在客户端使用实现标准化 API 的服务时很常见 (例如电子邮件、日历、电子健康 或开放银行),并且客户端由用户或 管理员进行配置。¶
攻击者可能入侵资源服务器,以访问相应部署的 资源。此类入侵的程度可以从 对系统的部分访问(例如其日志文件),到完全 控制相应服务器;在后一种情况下,所有控制措施都可以被 绕过,并且所有资源都可以 被访问。攻击者还能够获得遭入侵系统上持有的其他访问 令牌,而这些令牌可能对访问 其他资源服务器有效。¶
通过加固和监控服务器 系统来防止服务器遭到入侵 被视为标准运维过程,因此不属于 本文档的范围。 第 4.9 节重点讨论 OAuth 相关入侵产生的影响以及捕获的访问令牌被重放的问题。¶
为了应对恶意行为者进行的访问令牌重放,实现者 应考虑采取 以下措施:¶
攻击者可以通过多种方式窃取访问令牌,例如, 通过第 4.1、 4.2、4.3、4.4 和 4.9 节所述的攻击。其中一些攻击可以通过 相应章节所述的 特定安全措施来缓解。 但是,在某些情况下,这些措施并不足够,或者没有 正确实现。因此,授权服务器应该确保 访问令牌具有发送者约束和受众限制,如 下文所述。出于架构和性能方面的原因, 某些部署可能无法使用这些措施。¶
顾名思义,发送者约束访问令牌将 访问令牌的适用范围限制到特定发送者。该发送者 必须证明其知道某个特定秘密,作为 资源服务器接受该令牌的先决条件。¶
典型流程如下:¶
OAuth 工作组已经定义并在实践中使用了两种 通过持有证明实现发送者约束访问令牌的方法:¶
请注意,当 攻击者同时获得令牌和密钥材料时,发送者约束令牌的安全性就会受到破坏。特别是, 客户端软件遭到破坏以及跨站 脚本攻击(当客户端运行在浏览器中时)就属于这种情况。如果 密钥材料受到硬件或软件安全模块保护,或者 只能间接访问(例如在 TLS 栈中),那么发送者约束 令牌至少能够在客户端 离线时防止令牌被使用,即安全模块或接口无法被 攻击者使用时。这既适用于访问令牌,也适用于刷新 令牌(参见 第 4.14 节)。¶
受众限制本质上将访问令牌限制为 某个特定资源服务器。授权服务器将 访问令牌与特定资源服务器关联,随后资源 服务器应验证预期受众。如果访问令牌未通过 预期受众验证,资源服务器就拒绝处理 相应请求。¶
一般来说,受众限制能够限制令牌 泄露的影响。 在伪造资源服务器的情况下,它还可能(如下所述) 防止被钓取的访问令牌在 合法资源服务器上遭到滥用。¶
受众可以使用逻辑名称或 物理地址(例如 URL)表示。为了防止网络钓鱼,必须 使用客户端实际要向其发送请求的 URL。在 网络钓鱼场景中,该 URL 将指向伪造的资源 服务器。如果攻击者试图在 合法资源服务器(其 URL 不同)上使用该访问令牌,则资源 服务器会检测到不匹配(受众错误),并拒绝处理 请求。¶
在授权服务器知道所有 资源服务器 URL 的部署中,授权服务器可以直接拒绝为 未知资源服务器 URL 签发 访问令牌。¶
为了实现这一点,客户端需要告知授权服务器 预期的 资源服务器。可以使用 [RFC8707] 中的机制来实现,或者将该 信息编码在 scope 值中(第 3.3 节,见 [RFC6749])。¶
除了 URL,还可以使用 资源服务器 X.509 证书的指纹作为受众值。此 变体还可以检测通过使用从不同 CA 获得的 有效 TLS 证书来伪造合法 资源服务器 URL 的尝试。隐藏 资源服务器 URL,不让授权服务器获知,也可能被视为一种隐私优势。¶
受众限制可能看起来更容易使用,因为客户端侧不需要 任何加密操作。但是,由于每个访问令牌都 绑定到特定资源服务器,因此客户端在访问多个资源 服务器时,也需要为每个资源服务器分别获得 一个专用访问令牌。([RFC8707] 中规定的资源指示符可帮助实现 这一点。) [TOKEN-BINDING] 具有同样的特性,因为不同的 token-binding ID 必须与访问令牌关联。另一方面,使用 OAuth 2.0 双向 TLS [RFC8705] 则允许客户端在多个资源服务器上使用同一个 访问令牌。¶
需要注意的是,受众限制——或者更一般地说, 客户端向授权服务器指明其希望在哪里 使用访问令牌——除了防止 令牌泄露之外,还有其他好处。它允许授权服务器创建 一个不同的访问令牌,其格式和内容是专门为 相应服务器签发的。在使用结构化访问令牌的部署中,这具有巨大的功能和隐私 优势。¶
授权服务器可以向客户端提供有关 哪些位置可以安全使用其访问 令牌的额外信息。下面讨论这种方法以及为什么不建议 使用它。¶
最简单的形式是要求授权服务器
发布其已知
资源服务器的列表,以下示例使用非标准授权服务器元数据参数 resource_servers
进行说明:¶
授权服务器还可以在
令牌响应中返回访问
令牌适用的 URL,以下示例使用非标准返回
参数 access_token_resource_server 进行说明:¶
这种缓解策略将依赖客户端执行
安全策略,并且只将访问令牌发送到合法
目标。OAuth 相关安全研究的结果(例如,参见
[research.ubc] 和
[research.cmu])表明,很大一部分客户端实现没有正确实现或根本没有
实现诸如 state 检查这样的安全控制。因此,依赖
客户端防止访问令牌网络钓鱼同样很可能失败。
此外,考虑到客户端与授权服务器和资源服务器之间的数量比例,
将尽可能多的
安全相关逻辑移到这些服务器上被认为是更可行的做法。
显然,客户端
必须为整体安全做出贡献。但是,还有替代
对策,如第 4.10.1 和 4.10.2 节所述,它们在相关参与方之间提供了
更好的平衡。¶
当授权服务器或客户端具有 开放重定向器时,可能发生以下攻击。有时会实现此类端点, 例如,在随后将用户重定向到外部 网站之前显示消息,或者在用户受到 登录提示等中断后,将用户重定向回其原本想访问的 URL。¶
客户端不得暴露开放 重定向器。攻击者可能使用开放 重定向器生成指向客户端的 URL,并利用它们 窃取授权码和访问令牌,如 第 4.1.2 节所述。另一种 滥用情形是生成看起来 指向客户端的 URL。这可能会诱骗用户信任该 URL 并在浏览器中打开它。这可被用于网络钓鱼。¶
为防止开放重定向,客户端只应在 目标 URL 被允许,或者请求的来源和完整性 可以被认证时才执行重定向。OWASP [owasp.redir] 描述了针对开放重定向的对策。¶
在授权端点,一个典型的协议流程是 授权服务器 提示用户在表单中输入其凭据,然后 将该表单(使用 HTTP POST 方法)提交回授权 服务器。授权服务器检查凭据,如果成功,则将 用户代理重定向到客户端的重定向端点。¶
在 [RFC6749] 中,HTTP 状态码 302(Found)用于此目的,但 “允许使用用户代理可用的任何其他方法完成此 重定向”。如果改用状态码 307 进行重定向,用户代理将通过 HTTP POST 将用户凭据发送给客户端。¶
这会将敏感凭据泄露给客户端。如果客户端 是恶意的,它可以使用这些凭据在 授权服务器处冒充用户。¶
这种行为可能出乎开发者的预期,但它在 第 15.4.8 节,见 [RFC9110] 中有明确定义。此状态 码(307)不要求用户 代理将 POST 请求改写为 GET 请求,因此也不会丢弃 POST 请求正文中的表单数据。¶
在 HTTP 标准 [RFC9110] 中,只有状态码 303 明确要求将 HTTP POST 请求改写为 HTTP GET 请求。 对于所有其他状态码,包括常见的 302,用户 代理可以选择不将 POST 请求改写为 GET 请求,从而 导致用户凭据泄露给客户端。(但是在实践中,大多数 用户代理只有在 307 重定向时才会表现出这种行为。)¶
因此,重定向可能包含 用户凭据的请求的授权服务器不得使用 HTTP 307 状态码进行重定向。如果对这类 请求使用 HTTP 重定向(而不是例如 JavaScript),授权服务器应该使用 HTTP 状态码 303 (See Other)。¶
HTTP 应用的一种常见部署架构是将 应用服务器隐藏在一个反向代理之后,由该代理终止 TLS 连接,并将传入请求分发到相应的 应用服务器节点。¶
本节重点介绍这种部署 架构中与 OAuth 相关的一些攻击面,并给出 安全控制方面的建议。¶
在某些情况下,反向代理需要将安全相关 数据传递给上游应用服务器以供进一步处理。 示例包括请求发起者的 IP 地址、token-binding ID 以及经过认证的 TLS 客户端证书。此类数据通常通过 添加到上游请求中的 HTTP 标头传递。虽然这些标头 通常是自定义的、特定于应用的标头,但 [RFC9440] 中定义了用于客户端证书和客户端证书链的标准化标头 字段。¶
如果反向代理直接传递任何来自
外部的标头,攻击者就可能尝试通过代理将伪造的标头
值直接发送给应用服务器,以
绕过安全控制。例如,反向代理通常会
接受 X-Forwarded-For 标头,并仅仅
添加传入请求的来源(形成一个列表)。根据
应用服务器中执行的逻辑,攻击者可以
简单地向标头中添加一个允许的 IP 地址,使保护措施失效。¶
因此,反向代理必须清理所有 传入请求,以确保 与应用服务器安全相关的所有标头值都具有真实性和完整性。¶
如果攻击者能够访问 代理与应用服务器之间的内部网络,那么攻击者还可能尝试 绕过已有安全控制。因此,必须 确保通信实体的真实性。此外, 反向代理与应用服务器之间的通信链路必须受到保护,以防止消息遭到窃听、注入和重放。¶
刷新令牌是一种方便且对用户友好的获取新访问 令牌的方式。它们也有助于 提高 OAuth 的安全性,因为它们允许授权服务器签发 生命周期较短且范围缩小的访问令牌,从而降低 访问令牌泄露可能造成的影响。¶
刷新令牌对攻击者很有吸引力,因为它们 代表授予 某个特定客户端的完整访问范围,并且没有进一步限制到某个特定 资源。 如果攻击者能够窃取并成功重放 刷新令牌,攻击者就能够生成访问令牌,并使用 它们代表资源所有者访问资源服务器。¶
[RFC6749] 还通过定义相应的 错误码和响应行为,为进一步的 (特定于实现的)安全措施奠定了基础,例如刷新令牌过期和 撤销,以及刷新令牌轮换。¶
授权服务器必须根据风险评估确定 是否向某个客户端签发刷新令牌。如果 授权服务器决定不签发刷新令牌,则客户端可以通过使用其他许可类型(例如 授权码许可类型)获得新的访问令牌。在这种情况下,授权 服务器可以使用 Cookie 和持久许可来优化用户 体验。¶
如果签发刷新令牌,则这些刷新令牌必须绑定到 资源所有者同意的 scope 和资源服务器。 这样做是为了防止合法客户端提升权限,并降低 刷新令牌泄露的影响。¶
对于机密客户端,[RFC6749] 已经要求刷新 令牌只能由获得这些令牌的客户端使用。¶
授权服务器必须使用以下 方法之一来 检测恶意行为者针对公共客户端进行的刷新令牌重放:¶
刷新令牌轮换: 授权服务器在每次访问令牌刷新响应中签发一个新的 刷新令牌。此前的刷新令牌会被使其失效,但有关二者 关系的信息仍由授权服务器保留。如果刷新 令牌遭到泄露,并随后被攻击者 和合法客户端同时使用,其中一方会提交一个已经失效的 刷新令牌,从而通知授权服务器发生了 泄露。授权服务器无法确定哪一方 提交了无效刷新令牌,但会撤销 当前有效的刷新令牌。这会阻止攻击,但代价是迫使 合法客户端重新获得新的授权许可。¶
实现说明:刷新令牌所属的许可 可以编码到刷新令牌本身。这可以使 授权服务器高效确定某个刷新令牌所属的许可, 并进一步确定所有需要撤销的刷新令牌。 在这种情况下,授权服务器必须确保 刷新令牌值的完整性,例如 使用签名。¶
发生安全事件时,授权服务器可以自动撤销刷新 令牌,例如:¶
如果客户端一段时间未活动,刷新令牌应该过期, 即该刷新令牌已有一段时间未用于获取新的访问 令牌。过期时间由 授权服务器自行决定。它可以是一个全局值,也可以根据 客户端策略或与刷新令牌关联的许可 (及其敏感程度)确定。¶
资源服务器可能依据获得访问令牌的
资源所有者的身份,或者依据
客户端凭据许可中的客户端身份作出访问控制决策。例如,[RFC9068](OAuth
2.0 访问令牌的 JSON Web
Token(JWT)配置文件)描述了一种访问
令牌数据结构,其中包含一个 sub claim,其定义如下:¶
在通过涉及资源所有者的许可(例如授权码许可) 获得访问令牌的 情况下,“sub”的值应该对应资源 所有者的主体标识符。 在通过不涉及 资源所有者的许可(例如客户端凭据许可) 获得访问令牌的情况下,“sub”的值应该对应授权服务器用于指示 客户端应用的标识符。¶
如果两种情况都可能出现,资源服务器可能会将客户端的
身份
误认为资源所有者的身份。例如,如果客户端可以在向授权服务器注册时自行选择
client_id,
恶意客户端可能会将其设置为标识资源所有者的值(例如,
使用 OpenID Connect 时的
sub 值)。如果资源服务器无法正确
区分由资源
所有者参与而获得的访问令牌和没有资源所有者参与而获得的访问令牌,则客户端可能意外获得访问
资源所有者资源的能力。¶
如 第 4.4.1.9 节,见 [RFC6819] 所述, 授权请求容易受到点击劫持攻击,也称为用户界面重绘攻击。在 此类攻击中,攻击者将授权端点用户界面嵌入 一个看似无害的上下文中。用户以为自己正在与该上下文交互, 例如点击按钮时,实际上却无意中与授权 端点用户界面交互。反过来也可以做到:用户 以为自己正在与授权端点交互,却可能无意中在覆盖于原始用户 界面之上的、由攻击者提供的输入字段中输入 密码。点击劫持攻击可以设计得让用户几乎无法 察觉,例如使用几乎不可见的 iframe 覆盖在 其他元素之上。¶
攻击者可以利用这种方式获取用户的认证 凭据, 更改授予客户端的访问范围,并可能访问 用户的资源。¶
授权服务器必须防止点击劫持 攻击。[RFC6819] 中描述了多种 对策,包括使用 X-Frame-Options HTTP 响应标头字段和防止框架嵌入的 JavaScript。除此之外,授权服务器还应该 使用内容安全策略(CSP)Level 2 [W3C.CSP-2] 或更高版本。¶
要使其生效,必须在授权端点以及 适用时用于认证用户和 授权客户端的其他端点(例如设备授权端点、登录 页面、错误页面等)上使用 CSP。这可防止支持 CSP 的用户代理 被未授权的 源嵌入框架。客户端可以允许被与其重定向 端点所使用源不同的其他源嵌入 框架。因此,授权服务器应该允许 管理员为特定客户端配置允许的源, 和/或允许客户端动态注册这些源。¶
使用 CSP 可以让授权服务器在
单个响应标头字段中指定多个源,并使用灵活的
模式限制这些源(详情参见 [W3C.CSP-2])。
CSP Level 2 通过同时使用
限制框架来源的策略(使用 frame-ancestors)
和限制允许在 HTML 页面上执行的脚本来源的策略
(使用 script-src),提供了一种
防止点击劫持的稳健机制。下面的清单展示了一个非规范性
策略示例:¶
由于某些用户代理不支持 [W3C.CSP-2],除非授权服务器明确 不支持此类旧版用户代理,否则该技术 应该与其他技术结合使用,包括 [RFC6819] 中描述的技术。即使在这种情况下,仍应该采用额外 对策。¶
如果授权响应通过浏览器内通信 技术 (例如 postMessage [WHATWG.postmessage_api])而不是 HTTP 重定向发送,则消息可能 被无意发送到恶意源,或被恶意源注入。¶
以下使用浏览器内 通信进行攻击的非规范性伪代码示例在 [research.rub] 中有所描述。¶
通过 postMessage 发送授权响应或令牌响应时,授权服务器将响应发送到通配符 源“*”,而不是客户端的源。如果响应发送到的窗口 受攻击者控制,则攻击者可以读取该 响应。¶
window.opener.postMessage(
{
code: "ABC",
state: "123"
},
"*" // opener 窗口中的任何网站都可以接收该消息
)
¶
通过 postMessage 发送授权响应或令牌响应时,授权服务器可能不会将 接收方源与重定向 URI 进行检查,而是例如将 响应发送给攻击者提供的源。这类似于 第 4.1 节中描述的攻击。¶
window.opener.postMessage(
{
code: "ABC",
state: "123"
},
"https://attacker.example" // 攻击者提供的值
)
¶
期望通过 postMessage 接收授权响应或令牌响应的客户端可能不会验证消息的发送方源。这 可能允许攻击者向客户端注入授权响应或令牌响应。¶
在恶意注入授权 响应的情况下,该攻击 是 第 4.7 节所述 CSRF 攻击的一种变体。第 4.7 节中所述的 对策同样适用于此攻击。¶
在恶意注入令牌响应的情况下, 第 4.10.1 节所述的发送者约束 访问令牌在某些情况下可以防止攻击,但通常还需要 第 4.17.2 节中所述的额外 对策。¶
将客户端接收方源与预注册 源进行比较时, 授权服务器必须按照 第 4.1.3 节所述使用精确字符串匹配。 授权服务器必须将 postMessage 发送到 可信的客户端接收方源,如以下非规范性示例所示:¶
window.opener.postMessage(
{
code: "ABC",
state: "123"
},
"https://client.example" // 使用明确的客户端源
)
¶
postMessage 中不得使用类似“*”的通配符源, 因为攻击者可以利用它们 将受害者的浏览器内消息泄露到恶意源。 这两项措施都有助于防止授权码和 访问令牌泄露(参见 第 4.1 节)。¶
客户端必须防止 浏览器内消息被注入客户端 接收端点。客户端必须使用精确字符串匹配, 将浏览器内消息的发起方源与授权 服务器源进行比较,如以下非规范性示例所示:¶
window.addEventListener("message", (e) => {
// 精确验证授权服务器源
if (e.origin === "https://honest.as.example") {
// 处理 e.data.code 和 e.data.state
}
})
¶
由于浏览器内通信流程只是采用了不同的 通信 技术(即使用 postMessage 而不是 HTTP 重定向),因此 第 2.1 节中列出的所有保护授权响应的措施必须 同样适用。¶
我们感谢 Brock Allen、 Annabelle Richard Backman、 Dominick Baier、 Vittorio Bertocci、 Brian Campbell、 Bruno Crispo、 William Dennis、 George Fletcher、 Matteo Golinelli、 Dick Hardt、 Joseph Heenan、 Pedram Hosseyni、 Phil Hunt、 Tommaso Innocenti、 Louis Jannett、 Jared Jennings、 Michael B. Jones、 Engin Kirda、 Konstantin Lapine、 Neil Madden、 Christian Mainka、 Jim Manico、 Nov Matake、 Doug McDorman、 Karsten Meyer zu Selhausen、 Ali Mirheidari、 Vladislav Mladenov、 Kaan Onarioglu、 Aaron Parecki、 Michael Peck、 Johan Peeters、 Nat Sakimura、 Guido Schmitz、 Jörg Schwenk、 Rifaat Shekh-Yusef、 Travis Spencer、 Petteri Stenius、 Tomek Stojecki、 David Waite、 Tim Würtele 和 Hans Zandbelt 提供的宝贵反馈。¶