| 互联网草案 | 基于浏览器的应用的 OAuth 2.0 | 2025 年 12 月 |
| Parecki 等 | 2026 年 6 月 7 日过期 | [页] |
本规范详细说明了在开发使用 OAuth 2.0 的基于浏览器的应用时必须 考虑的威胁、攻击后果、安全注意事项和最佳实践。¶
本说明将在发布为 RFC 之前移除。¶
本文档的讨论在 Web 授权协议工作组邮件列表 (oauth@ietf.org) 上进行, 其归档位于 https://mailarchive.ietf.org/arch/browse/oauth/。¶
本草案的源代码和问题跟踪器可在 https://github.com/oauth-wg/oauth-browser-based-apps 找到。¶
本互联网草案完全按照 BCP 78 和 BCP 79 的 规定提交。¶
互联网草案是互联网工程任务组 (IETF) 的工作文档。 请注意,其他组织也可以将工作文档作为互联网草案分发。 当前互联网草案的列表位于 https://datatracker.ietf.org/drafts/current/。¶
互联网草案是最长有效期为六个月的草案文档, 并且可能随时由其他文档更新、替换或废止。 将互联网草案用作参考资料,或将其作为除“进行中的工作”之外的 其他内容来引用,都是不恰当的。¶
本互联网草案将于 2026 年 6 月 7 日过期。¶
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.¶
本规范描述了在浏览器中执行的应用中实现 OAuth 2.0 客户端的不同架构模式。本规范概述了基于浏览器的应用所面临的安全挑战,并分析了 不同模式如何帮助应对其中一些 挑战。¶
本文档重点关注作为 OAuth 客户端的 JavaScript 前端应用 (定义见 [RFC6749] 的 第 1.1 节),它与 授权服务器([RFC6749] 的 第 1.1 节)交互以 获取访问令牌,并可选择获取刷新令牌。客户端使用访问令牌访问 资源服务器上的受保护资源([RFC6749] 的 第 1.1 节)。 使用 OAuth 时,客户端、授权服务器和资源服务器都被视为独立 参与方,无论它们是否由同一实体拥有或运营。¶
请注意,许多 Web 应用由运行在共同 域上的前端和 API 组成,从而允许一种不依赖 OAuth 2.0 的架构。 这种情况在第 7.1 节中有更详细的描述。此类场景可以依赖 OpenID Connect [OpenID] 进行联合用户 认证,之后应用维护用户的认证状态。此类场景 (它仅将 OAuth 2.0 用作 OpenID Connect 的底层规范)不属于 本规范的范围。¶
对于使用 OAuth 2.0 和 OpenID Connect 的原生应用开发者,IETF BCP (当前最佳实践)已经发布,用于指导这些技术的集成。 该文档正式称为 [RFC8252] 或 BCP212,但通常按 OpenID Foundation 赞助的一组帮助开发者采用 这些实践的库称为“AppAuth”。[RFC8252] 针对如何在原生 应用中安全实现 OAuth 客户端提出了具体 建议,包括在需要时纳入额外的 OAuth 扩展。¶
本规范《基于浏览器的应用的 OAuth 2.0》强调了 基于浏览器的应用的安全属性与原生 应用的安全属性存在巨大差异,同时也讨论了将 OAuth 客户端实现为原生 应用和基于浏览器的应用之间的相似性。除 OpenID Connect 提供额外注意事项的地方外,本文档主要聚焦于 OAuth。¶
这些建议中的许多都源自 OAuth 2.0 安全性的当前最佳实践 [RFC9700],因为基于浏览器的应用也被 期望遵循这些建议。 本文档扩展并进一步限制了 [RFC9700] 中给出的各种建议。¶
本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、 “MAY”和“OPTIONAL”,当且仅当它们如这里所示以全大写形式 出现时,应按照 BCP 14 [RFC2119] [RFC8174] 中的描述解释。¶
本规范使用 OAuth 2.0 [RFC6749] 定义的“访问令牌”、“授权端点”、 “授权许可”、“授权服务器”、“客户端”、“客户端标识符”(客户端 ID)、“受保护 资源”、“刷新令牌”、“资源所有者”、“资源服务器”和“令牌端点”等术语,以及 [RFC6750] 定义的“持有者令牌”。¶
除引用规范中定义的术语外,本文档还使用 以下术语:¶
一种动态下载并在 Web 浏览器中执行的应用, 通常用 JavaScript 编写。也有时被称为“单页应用”或 “SPA”。¶
本文档讨论基于浏览器的应用的安全性,这些应用由 浏览器在运行时环境中执行。在大多数场景中,这些应用是在 JavaScript 执行环境中运行的 JavaScript(JS)应用。鉴于这种场景的普遍性,本文档使用 “JavaScript”一词指代所有允许代码在浏览器中应用 运行时内执行的机制。本文档中的建议和注意事项并不 只与 JavaScript 语言或其运行时相关,也适用于浏览器中的其他语言和 运行时环境,例如 Web Assembly([W3C.wasm-core-2])。¶
在 OAuth 2.0 最初于 [RFC6749] 和 [RFC6750] 中规定时,基于浏览器的 JavaScript 应用需要一种 严格遵守同源策略的解决方案。OAuth 2.0 的常见部署涉及一个 运行在不同于授权服务器域上的应用,因此历史上无法使用授权码 授权类型([RFC6749] 的 第 4.1 节), 因为它需要跨源 POST 请求。这一限制是定义隐式流程([RFC6749] 的 第 4.2 节)的 动机之一,该流程通过 URL 的片段部分在 前端通道中返回访问令牌,从而绕过对跨源 POST 请求的需求。¶
然而,隐式流程存在若干缺点,通常涉及 访问令牌暴露在 URL 中所带来的漏洞。有关这些攻击以及 在浏览器中使用隐式流程的缺点分析,请参见第 7.2 节。 其他攻击和安全注意事项可在 [RFC9700] 中找到。¶
在现代 Web 开发中,跨源资源共享(CORS)的广泛采用 [Fetch](它使同源 策略可以有例外)允许基于浏览器的应用使用 OAuth 2.0 授权码流程,并向 令牌端点发送 POST 请求以用授权码交换访问令牌。由于 授权码授权类型允许使用刷新令牌,这一行为也已被 基于浏览器的客户端采用,尽管这些客户端仍然是公共客户端(定义见 [RFC6749] 的 第 2.1 节), 并且很少或没有对安全 存储的访问能力。此外,在流程中加入 Proof Key for Code Exchange(PKCE) [RFC7636] 可以防止授权码注入,并且 确保即使授权码被截获,攻击者也无法使用它。¶
因此,并基于其他经验教训,当前针对 基于浏览器的应用的最佳实践是使用带有 PKCE 的 OAuth 2.0 授权码授权类型。 部署基于浏览器的应用有各种架构模式,既可以带有 对应的服务器端组件,也可以不带。每种架构都有特定的权衡和 注意事项,本文档后续会进一步讨论。对于 第一方共同域应用,还适用其他注意事项。¶
恶意 JavaScript 对基于浏览器的应用构成重大风险。攻击 向量,例如跨站脚本(XSS)或远程代码文件被攻陷,会使攻击者 能够在应用的执行上下文中运行任意代码。该恶意代码 不会以任何方式与主应用代码隔离。因此,恶意代码不仅可以 控制正在运行的执行上下文,还可以在应用的 源内执行操作。具体而言,这意味着恶意代码可以从当前页面窃取数据,与 其他同源浏览上下文交互,从应用的 源内向后端发送请求,从基于源的存储机制(例如 localStorage、IndexedDB)窃取数据,等等。¶
首先也是最重要的是,采取主动措施防止攻击者 首先立足是至关重要的。这样做包括但不限于:¶
更多建议可在 OWASP Cheat Sheet 系列 [OWASPCheatSheet] 中找到。¶
遗憾的是,历史表明,即使应用这些安全指南, 仍然存在攻击者找到某种方式触发恶意 JavaScript 执行的风险。在 存在恶意 JS 的情况下分析基于浏览器的应用安全性时,必须 认识到恶意 JavaScript 代码与合法 应用代码具有相同权限。所有 JS 应用在某种程度上都面临此风险。¶
应用可能会获取授予其运行所需授权的 OAuth 令牌。 综合来看,这实际上使被攻陷的代码能够将该授权用于恶意目的。 尽管攻击者滥用授权的风险不可避免,但仍有一些方法可以 限制被攻陷应用能够滥用该授权的程度。例如,可以将这种 访问限制在应用正在主动使用的时间内,通过限制可能获得的令牌 类型,或通过将令牌绑定到浏览器来实现。¶
当合法应用代码可以访问变量或调用函数时, 恶意 JS 代码也可以做完全相同的事情。此外,恶意 JS 代码可以篡改 应用的常规执行流程,以及任何应用级防御,因为这些防御 通常从应用内部进行控制。例如,攻击者可以移除或覆盖 事件监听器,修改内置函数的行为(原型污染),并停止 框架中的页面加载。¶
恶意 JavaScript 对基于浏览器的应用的影响是一个被广泛研究且 理解充分的主题。然而,恶意 JavaScript 对作为 OAuth 客户端的 基于浏览器的应用的具体影响相当独特,因为恶意 JavaScript 现在可以影响 OAuth 流程期间的交互。本节探讨恶意 JS 代码对 承担 OAuth 客户端职责的基于浏览器的应用构成的威胁。第 5.1 节讨论攻击者一旦 找到运行恶意 JavaScript 代码的方法后可以使用的几个场景。 这些场景清楚展示了攻击者的真实能力,这远远超出了简单的令牌外泄。第 5.2 节分析这些攻击场景对 OAuth 客户端的影响。¶
本规范的其余部分将在分析不同架构模式的 安全属性时回溯引用这些攻击场景和 后果。¶
本节给出攻击者在找到允许执行 恶意 JavaScript 代码的漏洞后可以执行的若干攻击场景。攻击 场景包括简单场景(第 5.1.1 节)和复杂场景(第 5.1.3 节)。请注意,此 枚举并不穷尽,范围狭窄地限定于 OAuth 特定功能,并且不按 特定顺序呈现。¶
此场景涵盖一种简单的令牌外泄攻击,其中 攻击者获取并外泄客户端当前的令牌。此场景包括 以下步骤:¶
降低与被攻陷访问令牌相关风险的推荐防御策略 是缩小令牌的作用域和生命周期。对于 刷新令牌,使用刷新令牌轮换(定义见 [RFC9700] 的 第 4.14.2 节)提供了一种 检测和纠正机制。发送者约束令牌(第 9.2 节)为 防御被盗访问令牌提供了额外一层保护。¶
请注意,此攻击场景很简单,并且常被用来 说明恶意 JavaScript 的危险。在讨论 基于浏览器的应用的安全性时,必须避免将攻击者的能力 限定为本场景中讨论的攻击。¶
此攻击场景是 单次执行令牌窃取场景(第 5.1.1 节)的更高级变体。攻击者不是在执行 恶意代码后立即窃取令牌,而是设置必要的 处理程序,以持续窃取应用的令牌。此场景包括 以下步骤:¶
此场景中的关键差异是,攻击者始终 能够访问应用使用的最新令牌。攻击场景中的这种轻微变化 已足以抵消针对令牌窃取的典型防御,例如 较短的生命周期或刷新令牌轮换。¶
对于访问令牌,只要用户的浏览器在线,攻击者现在就可以获取 最新的访问令牌。刷新令牌轮换不足以 防止刷新令牌被滥用。攻击者可以轻易确保应用 不会使用最新的刷新令牌。例如,攻击者可以在窃取令牌后清除 应用的令牌,等待用户关闭应用,或 等待用户的浏览器离线。由于应用不会使用 最新的刷新令牌,因此不会出现可检测的刷新令牌重用,从而让 攻击者完全控制被盗刷新令牌。¶
在此高级攻击场景中,攻击者完全无视 应用已经获得的任何令牌。相反,攻击者利用 能够运行与应用 源相关联的恶意代码的能力。借助这种能力,攻击者可以注入一个隐藏 iframe,并启动一个静默 授权码流程。此静默流程将重用用户与 授权服务器之间的现有会话,并导致签发新的、独立的访问令牌 (以及可选的刷新令牌)。此场景包括以下步骤:¶
执行恶意 JS 代码¶
设置处理程序以从 iframe 获取授权码(例如,通过监控 frame 的 URL 或通过 Web Messaging [WebMessaging])¶
将隐藏 iframe 插入页面,并用授权请求对其进行初始化。 iframe 中的授权请求将在用户的会话内 发生,并且如果会话仍然处于活动状态,将导致签发 授权码。请注意,此步骤依赖于 授权服务器支持基于静默 frame 的流程,如本场景 最后一段所讨论。¶
使用先前安装的处理程序从 iframe 中提取 授权码¶
将授权码发送到攻击者控制的 服务器¶
用授权码交换一组新的 令牌¶
滥用被盗令牌¶
从此场景中得到的最重要结论是,它运行的是一个新的 OAuth 流程,而不是专注于窃取现有令牌。本质上,即使 应用找到一种能够将 存储的令牌与攻击者完全隔离的令牌存储机制,攻击者仍然能够请求一组新的 令牌。请注意,由于攻击者控制了浏览器中的应用, 攻击者的授权码流程与合法的授权码流程无法区分。¶
此攻击场景之所以可能,是因为公共 基于浏览器的 OAuth 客户端的安全性完全依赖于重定向 URI 和应用的 源。当攻击者在应用的 源中执行恶意 JavaScript 代码时,他们获得了检查同源 frame 的能力。因此, 攻击者在主执行上下文中运行的代码可以检查 加载在同源 frame 中的重定向 URI,以提取授权码。¶
没有实际可行的前端应用安全机制 能够对抗此攻击场景。较短的访问令牌生命周期和刷新令牌 轮换无效,因为攻击者拥有一组全新的、独立的令牌。 高级安全机制,例如 DPoP [RFC9449] 同样无效,因为 攻击者可以使用自己的密钥对为新获取的令牌设置并使用 DPoP。 要求每次授权码流程都需要用户交互,将有效阻止 自动静默签发新令牌,但这会严重影响 广泛建立的模式,例如在首次页面 加载时引导应用,或跨多个相关应用进行单点登录,因此不是实际可行的 措施。¶
此攻击场景涉及攻击者从运行在 用户浏览器中的 OAuth 客户端应用内部直接向 OAuth 资源服务器发送请求。在此场景中,攻击者无需滥用 应用来获取令牌,因为浏览器会在请求中附带自己的 Cookie 或令牌。 攻击者发送给资源服务器的请求与合法应用发送的请求 无法区分,因为攻击者在与合法应用相同的上下文中运行代码。 此场景包括以下步骤:¶
为授权发往资源服务器的请求,攻击者 只需模仿客户端应用的行为。例如,当客户端 应用以编程方式将访问令牌附加到传出请求时,攻击者 也会这样做。如果客户端应用依赖外部组件来为 请求附加适当的访问令牌,那么该外部组件也会增强 攻击者的请求。¶
这种攻击模式众所周知,也会出现在使用
HttpOnly 会话 Cookie 的传统
应用中。通常认为这种
场景无法通过应用级安全措施停止或预防。例如,
DPoP [RFC9449] 明确
认为此攻击场景超出范围。¶
成功执行某个攻击场景可能导致访问 令牌和刷新令牌被窃取,或者导致攻击者能够劫持运行在 用户浏览器中的客户端应用。这些后果都与基于浏览器的 OAuth 客户端相关。它们 按严重程度递减的顺序在下文讨论。¶
当攻击者从基于浏览器的 OAuth 客户端获取有效刷新令牌时,他们可以通过与 授权服务器运行刷新令牌授权来滥用该刷新令牌。刷新令牌授权的 响应包含访问令牌,这使攻击者能够访问受保护资源 (参见第 5.2.2 节)。本质上, 滥用被盗刷新令牌使攻击者能够长期冒充合法客户端 应用访问资源服务器。¶
只有当授权服务器因刷新令牌已过期或已轮换而拒绝该 刷新令牌,或当刷新令牌被撤销时,攻击才会停止。 在典型的基于浏览器的 OAuth 客户端中,刷新令牌 保持有效数小时甚至数天并不罕见。¶
如果攻击者获取了有效访问令牌,他们就获得了 在向资源服务器发起请求时冒充合法客户端应用的能力。 具体而言,持有访问令牌允许攻击者向任何接受该有效访问令牌的 资源服务器发送任意请求。本质上,滥用被盗 访问令牌使攻击者能够短期冒充合法客户端应用访问 资源服务器。¶
当访问令牌过期或令牌在 授权服务器处被撤销时,攻击结束。在典型的基于浏览器的 OAuth 客户端中,访问 令牌生命周期可以相当短,从几分钟到几小时不等。¶
请注意,持有访问令牌允许攻击者不受限制地 使用它。攻击者可以向资源服务器发送任意请求,使用 任意 HTTP 方法、目标 URL、标头值或正文。¶
应用可以使用 DPoP 来确保其访问令牌绑定到 浏览器持有的不可导出密钥。在这种情况下,攻击者滥用 被盗访问令牌会显著更加困难。更具体地说,使用 DPoP 时, 攻击者只能通过实施在线攻击来滥用被盗应用令牌, 其中证明是在用户浏览器中计算的。此攻击在 [RFC9449] 的 第 11.4 节中有详细描述。然而,当攻击者获取 新的访问令牌(以及可选的刷新令牌),如第 5.1.3 节所述时,他们可以使用 攻击者控制的密钥对为这些令牌设置 DPoP。在这种情况下, 攻击者同样可以不受限制地滥用这个新获得的访问令牌。¶
当窃取令牌不可行或并非所需时,攻击者也可以 选择劫持运行在用户浏览器中的 OAuth 客户端应用。这 实际上允许攻击者执行合法客户端 应用可以执行的任何操作。示例包括检查页面上的数据、修改 页面,以及向后端系统发送请求。或者,攻击者也可以 滥用其对应用的访问来发起其他攻击,例如诱骗 客户端使用诸如会话固定 ([SessionFixation])之类的攻击代表攻击者行事。¶
请注意,客户端劫持不如直接滥用
被盗用户令牌强大。在客户端劫持场景中,攻击者不能直接控制
令牌,并受限于客户端
应用上强制执行的安全策略。例如,运行在 admin.example.org
上的资源服务器可以配置 CORS 策略,以拒绝来自运行
在 web.example.org 上的客户端的请求。即使客户端使用的访问令牌
会被资源服务器接受,资源服务器严格的 CORS 配置
也不允许这样的请求。没有此类严格 CORS 策略的资源服务器仍然
可能受到来自被攻陷客户端应用的对抗性请求影响。¶
构建依赖 OAuth 来访问受保护资源的基于浏览器的 应用时,主要有三种可用的架构模式。¶
一种基于浏览器的应用,它依赖后端组件处理 OAuth 职责,并通过该后端组件转发所有请求 (Backend-For-Frontend 或 BFF)¶
一种基于浏览器的应用,它依赖后端组件处理 OAuth 职责,但使用访问令牌直接调用资源服务器 (令牌中介后端)¶
一种作为客户端的基于浏览器的应用,在浏览器中处理所有 OAuth 职责(基于浏览器的 OAuth 客户端)¶
这些架构模式各自在安全性和简单性之间提供了不同的权衡。 本节中的模式按安全性递减的顺序给出。¶
本节描述一种基于浏览器的应用架构,它 依赖后端组件处理所有 OAuth 职责和 API 交互。BFF 有三项核心职责:¶
BFF 在基于 Cookie 的会话上下文中管理 OAuth 访问令牌和刷新令牌, 避免任何令牌直接暴露给基于浏览器的 应用¶
BFF 将所有请求转发到资源服务器,并在转发到资源服务器之前 使用正确的访问令牌增强这些请求¶
在此架构中,BFF 作为服务器端组件运行,但它是 前端应用的一个组件。为了避免与 API 网关和反向代理等其他架构概念混淆, 需要记住,BFF 会成为前端应用的 OAuth 客户端。¶
如果攻击者能够在基于浏览器的 应用内执行恶意代码,该应用架构能够抵御先前讨论的大多数攻击场景。 由于令牌只对 BFF 可用,因此浏览器中没有可供提取的令牌 (单次执行令牌窃取(第 5.1.1 节)和持久性令牌窃取(第 5.1.2 节))。BFF 是 机密客户端,这会阻止攻击者在浏览器内运行新的流程 (新令牌的获取和提取(第 5.1.3 节))。由于恶意的基于浏览器的代码仍然 运行在应用的源内,攻击者能够从 用户的浏览器内部向 BFF 发送请求(通过用户的浏览器代理请求(第 5.1.4 节))。请注意,使用 HttpOnly Cookie 会阻止攻击者直接访问会话状态,从而阻止从客户端劫持升级为 会话劫持。¶
在此架构中,浏览器代码(通常是 JavaScript) 首先从静态 Web 主机加载到浏览器中(A),然后应用 在浏览器中运行。应用通过调用“检查会话”API 端点(B),向 BFF 检查是否存在活动会话。 如果找到活动会话,应用将恢复其已认证状态,并跳到步骤 J。¶
当没有找到活动会话时,基于浏览器的应用 触发导航到 BFF(C),以使用 PKCE 扩展启动授权码流程(见第 6.1.3.1 节),BFF 通过将浏览器重定向到 授权端点(D)来响应。用户被重定向回来时,浏览器将 授权码传递给 BFF(E),随后 BFF 可以使用其客户端凭据和 PKCE 代码验证器 在令牌端点(F)将其交换为令牌。¶
BFF 将获得的令牌与用户的会话关联(参见 第 6.1.2.3 节),并在响应中设置 Cookie 来跟踪此会话(G)。此时, 基于重定向的授权码流程已经完成,因此 BFF 可以将控制权 交还给前端应用。它通过在响应中包含重定向来做到这一点 (G),触发浏览器从服务器获取前端(H)。请注意,步骤 (H)与步骤(A)相同,这很可能意味着请求的资源可以 从浏览器缓存加载。当前端加载时,它会向 BFF 检查是否存在 会话(I),从而允许应用恢复其已认证状态。¶
当浏览器中的应用想要向 资源服务器发出请求时,它会向 BFF 上对应的端点发送请求(J)。该 请求将包含步骤 G 中设置的 Cookie,使 BFF 能够为该用户的会话获取合适的 令牌。BFF 从请求中移除 Cookie,将 用户的访问令牌附加到请求,并将其转发到实际的资源服务器 (K)。随后 BFF 将响应转发回基于浏览器的应用(L)。¶
BFF 提供一组端点,它们对于 实现基于浏览器的应用与 BFF 之间的交互至关重要。 本节稍微更详细地讨论这些端点,以阐明其 目的和用例。¶
“检查会话”端点(上图中的步骤 B 和 I) 是由基于浏览器的应用调用的 API 端点。请求 会在可用时携带会话信息,使 BFF 能够检查是否存在 活动会话。响应应向基于浏览器的应用表明 会话是否处于活动状态。此外,BFF 可以包含其他 信息,例如已认证用户的身份信息。¶
启动授权码流程的端点 (步骤 C)由浏览器通过导航联系。当应用 在检查会话(步骤 B)后检测到未认证状态时,它可以 将浏览器导航到此端点。这样做允许 BFF 使用 重定向进行响应,将浏览器带到授权服务器。用于 启动此流程的端点通常由支持在 Web 服务器上运行的机密客户端的 OAuth 2.0 库作为“login”端点提供。请注意, BFF 也可以在步骤 B 中检测到没有活动会话时启动授权码流程。 在这种情况下,BFF 会在响应中返回授权 URI,并期望应用 使用该 URI 触发导航事件。然而,此场景需要 自定义实现,并且更难使用标准 OAuth 库。¶
接收授权码的端点(步骤 E) 由浏览器内部的导航事件调用。此时, 应用未加载,且无法处理重定向。与 流程启动类似,处理重定向的端点由 标准 OAuth 库提供。BFF 可以用一个 触发浏览器加载应用的重定向来响应该请求。¶
最后,BFF 还可以向 应用提供一个“logout”端点,该端点未在上图中描绘。 logout 端点的确切行为取决于应用需求。请注意,标准 OAuth 库通常也会提供“logout” 端点的实现。¶
使用刷新令牌时,如 [RFC9700] 的 第 4.14 节所述,BFF 获取刷新令牌 (步骤 F)并将其与用户的会话关联。¶
如果 BFF 注意到用户的访问令牌已经 过期,并且 BFF 拥有刷新令牌,它可以使用该刷新令牌来获取 新的访问令牌。由于 BFF OAuth 客户端是机密客户端,它 会在刷新令牌请求中使用客户端认证。通常,BFF 在处理来自前端的 API 调用时内联执行这些步骤。在这种 情况下,这些未在图中明确显示的步骤会发生 在步骤 J 和 K 之间。将所有令牌信息保留在 服务器端可用的 BFF,也可以在观察到令牌 过期事件时请求新的访问令牌,以提升 API 请求性能。¶
当刷新令牌过期时,如果不运行一个全新的授权码流程, 就无法获得有效访问令牌。 因此,将 BFF 管理的基于 Cookie 的会话的生命周期 配置为等于刷新令牌的最长生命周期是合理的。 此外,当 BFF 得知某个活动会话的刷新令牌 不再有效时,使该会话失效也是合理的。¶
BFF 依赖浏览器 Cookie([I-D.ietf-httpbis-rfc6265bis])来跟踪 用户的会话,该会话用于访问用户的令牌。基于 Cookie 的 会话,无论是服务器端还是客户端,都有一些缺点。¶
服务器端会话只暴露一个会话标识符,并 将所有数据保留在服务器上。这样做可以确保对 活动会话具有很高程度的控制,并且可以随时撤销任何会话。 此方法的缺点是对可扩展性的影响,需要诸如 “sticky sessions”或“session replication”之类的解决方案。鉴于这些缺点,只有在小规模场景中才推荐 将服务器端会话与 BFF 一起使用。¶
客户端会话将所有数据以 签名且可选加密的对象推送到浏览器。此模式使服务器不必 跟踪任何会话数据,但会严重限制对活动 会话的控制,并使处理会话撤销变得困难。然而,当 客户端会话在 BFF 的上下文中使用时,这些属性会 显著改变。由于基于 Cookie 的会话仅用于获取用户的 令牌,所有控制和撤销属性都源自访问 令牌和刷新令牌的使用。撤销用户的访问令牌和/或 刷新令牌就足以阻止对受保护资源的持续访问,而无需 显式使基于 Cookie 的会话失效。¶
保护会话 Cookie 的最佳实践在 第 6.1.3.2 节中讨论。¶
此应用架构使用的 OAuth 流程可以
通过在授权请求(C)中包含必要的 OpenID Connect 作用域
与 OpenID Connect 结合(至少包含 [OpenID] 第 3.1.2.1 节定义的 openid
作用域)。
在这种情况下,BFF 会在步骤 F 中
接收 ID Token。BFF 可以将 ID Token 中的信息与用户的会话关联,
并在步骤 B 或 I 中将其提供给应用。¶
需要时,BFF 可以使用与用户会话关联的访问令牌 向 UserInfo 端点发出请求。¶
提供静态 JavaScript 代码与处理 OAuth 令牌和转发请求 是两项不同的职责。在上面给出的图中,BFF 和静态 Web 主机 显示为两个独立实体。在现实世界部署中,这些组件可以部署为 单个服务(即 BFF 提供静态 JS 代码)、两个独立 服务(即 CDN 和 BFF),或单个服务中的两个组件 (即云平台上的静态托管和无服务器函数)。¶
请注意,可以进一步定制此 架构以适配特定场景。例如,依赖内部和外部资源服务器的应用 可以选择将内部资源服务器与 BFF 一起托管。 在这种场景中,到内部资源服务器的请求直接在 BFF 处理, 无需通过网络转发请求。从 资源服务器的视角来看,授权不会改变,因为用户会话在内部会转换为 访问令牌及其声明。¶
使用 BFF 的主要好处是 BFF 能够 充当机密客户端。因此,BFF MUST 通过与授权服务器建立凭据来充当机密 客户端。此外, BFF MUST 使用 [RFC9700] 的 第 2.1.1 节中所述的 OAuth 2.0 授权码授权来发起 访问令牌请求。¶
基于浏览器的应用与 BFF 之间的交互依赖 Cookie 进行认证和授权。与其他 基于 Cookie 的交互类似,BFF 必须考虑跨站请求 伪造(CSRF)攻击。成功的 CSRF 攻击可能允许攻击者发往 BFF 的请求触发对受保护资源的传出调用。¶
BFF MUST 实现适当的 CSRF 防御。确切的 机制或机制组合取决于 BFF 部署所在的确切域, 如下文所讨论。¶
BFF 可以依赖 CORS 作为 CSRF 防御 机制。CORS 是浏览器实现的一种安全机制,它 限制跨源请求,除非服务器通过设置适当的 CORS 标头 显式批准此类请求。¶
浏览器通常会限制由脚本发起的跨源 HTTP 请求。如果目标服务器批准该请求,CORS 可以移除此限制, 这会通过初始的“preflight”请求进行检查。除非预检响应显式批准 该请求,否则浏览器会拒绝发送完整请求。¶
由于这一属性,BFF 可以依赖 CORS 作为 CSRF 防御。当攻击者尝试从用户的浏览器向 BFF 发起跨源 请求时,BFF 不会在预检响应中批准 该请求,从而导致浏览器阻止 实际请求。请注意,攻击者始终可以从自己的机器发起请求, 但此时请求不会携带用户的 Cookie,因此攻击会失败。¶
在依赖 CORS 作为 CSRF 防御时, 需要意识到某些请求可以不经过 预检。对于这类称为“CORS-safelisted Requests”的请求, 浏览器会直接发送请求,如果服务器没有发送适当的 CORS 标头, 则阻止对响应的访问。此行为 会针对可通过 JavaScript 以外的其他方式触发的请求强制执行, 例如 GET 请求或基于表单的 POST 请求。¶
此行为的后果是,即使启用了 CORS 作为防御, 资源服务器的某些端点也可能变得容易受到 CSRF 攻击。 例如,如果资源服务器是一个暴露无正文 POST 请求端点的 API, 则不会有预检请求,也不会有 CSRF 防御。¶
为了避免针对 CORS 策略的此类绕过,
BFF SHOULD 要求基于浏览器的应用包含一个
自定义请求标头。带有自定义请求
标头的跨源请求总是需要预检,这使 CORS 成为有效的 CSRF
防御。使用此机制时,BFF MUST 确保每个
传入请求都携带此静态标头。该标头的具体命名由应用和 BFF
自行决定。示例
配置可以是一个具有静态值的请求标头,例如
My-Static-Header: 1。¶
也可以将基于浏览器的 应用部署在与 BFF 相同的源上。这样可以确保前端和 BFF 之间的合法 交互不需要任何 预检,因此没有额外开销。¶
BFF 模式要求基于浏览器的 应用通过后端 BFF 组件将所有请求转发到资源服务器。 因此,BFF 组件能够观察应用和资源服务器之间的所有请求 和响应,这可能产生相当大的隐私影响。¶
当基于浏览器的应用和 BFF 由同一方构建和 部署时,隐私影响可能很小。然而,当 此模式使用由第三方提供或托管的 BFF 组件实现时, 就需要考虑这种隐私影响。¶
由于 BFF 会代表前端将所有请求转发到资源 服务器,因此应注意确保资源 服务器了解此组件,并为速率 限制和其他反滥用措施使用适当策略。例如,如果 BFF 作为 单实例服务部署,而资源服务器根据 IP 地址限制请求速率, 它可能会开始阻止请求,因为许多用户的浏览器看起来都来自 BFF 的单个 IP 地址。¶
BFF 通过接受来自 前端的请求并将其转发到资源服务器来充当代理服务。入站请求 携带 Cookie,BFF 会在出站 请求中将其转换为访问令牌。(请注意,这使它更像应用层反向代理, 而不是 HTTP 代理。)除了 CSRF 攻击外,攻击者还可能尝试 操纵 BFF,使其将请求转发到非预期主机。如果攻击者 成功利用这一点,他们可能会将 BFF 重定向到任意服务器, 从而可能暴露用户的访问令牌。¶
为缓解此风险,BFF MUST 通过在转发
请求前验证目标主机来强制执行严格的
出站请求控制。这需要维护一个经批准资源
服务器的显式允许列表,确保请求只被代理到预定义后端(例如,
/bff/orders/create 专门映射到
https://order-api.example.com/create)。如果需要基于
路径的动态路由(例如 /bff/orders/{id}),BFF MUST 进行
严格验证,以确保只有授权目标可访问。
此外,按端点限制允许的 HTTP 方法可以
进一步减少攻击向量。¶
实现动态可配置代理时, BFF MUST 确保它只允许请求显式许可的主机和 路径。未能强制执行这些限制可能导致未授权访问和 访问令牌泄漏。¶
本节重新审视第 5 节中的攻击场景和后果,并讨论潜在的 额外防御。¶
如果攻击者能够在应用的执行上下文中执行恶意代码 (例如 JavaScript 或 WASM),以下 攻击场景就会变得相关:¶
请注意,此攻击场景会导致以下 后果:¶
请注意,客户端劫持是一个 由基于浏览器的应用本质决定的攻击场景。因此,除了首先阻止恶意 代码执行之外,没有任何东西能够防止此类攻击。可以帮助实现这一点的技术包括 遵循安全编码指南、代码分析,以及部署深度防御 机制,例如内容安全策略([W3C.CSP3])。¶
在此架构中,BFF 是处理 各种安全特定职责和基于代理行为的关键组件。虽然 讨论基于代理应用的安全实现 超出了本文档范围,但必须注意,BFF 中的安全漏洞 可能对应用产生重大影响。¶
最后,BFF 处于一个独特位置,可以观察 基于浏览器的应用和资源服务器之间的所有流量。如果 高安全性应用希望实现异常检测或速率 限制,那么这样的 BFF 将是理想位置。此类限制可以 进一步帮助缓解客户端劫持的后果。¶
本节描述一种基于浏览器的应用架构,它 依赖后端组件以机密客户端身份处理获取令牌的 OAuth 职责 (定义见 [RFC6749] 的 第 2.1 节)。 随后,后端组件向应用提供访问令牌,使其能够直接与资源服务器交互。¶
令牌中介后端模式比 BFF 模式 更轻量(参见第 6.1 节),因为它不 要求代理应用与资源 服务器之间的所有请求和响应。从安全角度看,令牌中介后端不如 BFF 安全,但 相比直接在 浏览器中运行的 OAuth 客户端应用,仍具有显著优势。¶
如果攻击者能够在应用内执行恶意代码,该 应用架构能够防止攻击者滥用刷新令牌 (单次执行令牌窃取(第 5.1.1 节)和持久性令牌窃取(第 5.1.2 节)),或 获得一组新的令牌(新令牌的获取和提取(第 5.1.3 节))。然而,由于 访问令牌直接暴露给应用,攻击者可以从 客户端存储中窃取令牌(单次执行令牌窃取(第 5.1.1 节)和持久性令牌窃取(第 5.1.2 节)),或从 令牌中介后端请求新令牌(通过用户的浏览器代理请求(第 5.1.4 节))。请注意,使用 HttpOnly Cookie 会阻止攻击者直接访问会话状态,从而阻止 从访问令牌窃取升级为会话劫持。¶
在此架构中,基于浏览器的代码(例如 JavaScript 或 WASM)首先从静态 Web 主机加载到浏览器中(A),然后应用 在浏览器中运行。应用向令牌中介后端检查 是否存在活动会话(B)。如果找到活动会话,应用会接收 相应的访问令牌,恢复其已认证状态,并跳到 步骤 J。¶
当没有找到活动会话时,应用会触发 导航到令牌中介后端(C),以使用 PKCE 扩展启动授权码流程 (见第 6.2.3.1 节),令牌中介 后端通过将浏览器重定向到授权端点(D)来响应。当 用户被重定向回来时,浏览器将授权码传递给 令牌中介后端(E),随后令牌中介后端可以使用其客户端凭据和 PKCE 代码验证器 在令牌端点(F)将其交换为 令牌。¶
令牌中介后端将获得的令牌与 用户的会话关联(参见第 6.2.2.4 节),并在响应中设置 Cookie 来跟踪此会话(G)。 对浏览器的此响应还将触发应用重新加载(H)。 当此应用重新加载时,它会向令牌中介后端检查是否存在 现有会话(I),从而允许应用恢复其已认证状态,并 从令牌中介后端获取访问令牌。¶
浏览器中的应用可以使用在 步骤 I 中获得的访问令牌,直接向资源服务器发出请求(J)。¶
令牌中介后端的大多数端点实现 类似于 BFF 所描述的实现。¶
“检查会话”端点(上图中的步骤 B 和 I) 是由基于浏览器的 应用调用的 API 端点。请求会在可用时携带会话信息, 使后端能够检查是否存在活动会话。响应应 向基于浏览器的应用表明会话是否处于活动状态。 如果找到活动会话,后端会在 响应中包含访问令牌。此外,后端可以包含其他信息, 例如已认证用户的身份信息。¶
启动授权 码流程的端点(步骤 C)与 BFF 架构所描述的端点相同。更多细节参见第 6.1.2.1 节。¶
接收授权 码的端点(步骤 E)与 BFF 架构所描述的端点相同。更多细节参见第 6.1.2.1 节。¶
支持 logout 的端点与 BFF 架构所描述的端点相同。更多细节参见第 6.1.2.1 节。¶
使用刷新令牌时,如 [RFC9700] 的 第 4.14 节所述,令牌中介后端 在步骤 F 中获取刷新令牌,并将其与用户的会话关联。¶
如果资源服务器拒绝访问令牌, 应用可以联系令牌中介后端来请求新的访问 令牌。令牌中介后端依赖与此 请求关联的 Cookie 来查找用户的刷新令牌,并使用 刷新令牌发出令牌请求。这些步骤未在图中显示。请注意,此刷新 令牌请求来自后端,即机密客户端,因此需要 客户端认证。¶
当刷新令牌过期时,如果不启动一个全新的授权码授权, 就无法获得有效访问令牌。 因此,如果事先知道此类信息,将基于 Cookie 的会话的生命周期 配置为等于刷新令牌的最长生命周期是合理的。 此外,当令牌中介后端得知某个 活动会话的刷新令牌不再有效时,使会话 失效是合理的。¶
根据正在访问的资源服务器以及 授权服务器处的作用域配置,应用可能希望 请求具有不同作用域配置的访问令牌。这一行为 会允许应用遵循使用最小作用域 访问令牌的最佳实践。¶
应用可以在检查活动会话(步骤 A/I)时 将所需作用域告知令牌中介后端。由 令牌中介后端决定此前获得的访问令牌是否符合 所需的作用域条件。¶
需要注意,如果 不加区分地应用,令牌中介后端的这种访问令牌缓存 机制可能导致作用域提升风险。如果缓存的访问令牌具有前端请求作用域的超集, 令牌中介后端 SHOULD NOT 将其返回给前端;相反,它 SHOULD 使用刷新令牌从授权服务器请求一个具有较小作用域集合的 访问令牌。请注意,支持这种访问令牌下调作用域机制 由授权服务器自行决定。¶
与 BFF 类似,令牌中介后端依赖 浏览器 Cookie 来跟踪用户会话。适用于 BFF 的相同实现 指南和安全注意事项也适用,如第 6.1.2.3 节所讨论。¶
与 BFF 类似,令牌中介后端可以选择 在单个流程中组合 OAuth 和 OpenID Connect。更多细节参见第 6.1.2.4 节。¶
使用令牌中介后端的主要好处是 后端能够充当机密客户端。因此, 令牌中介后端 MUST 充当机密客户端。此外, 令牌中介后端 MUST 使用 [RFC9700] 的 第 2.1.1 节中所述的 OAuth 2.0 授权码授权 来发起访问令牌请求。¶
基于浏览器的应用与 令牌中介后端之间的交互依赖 Cookie 进行认证和 授权。与 BFF 一样,令牌中介后端必须 考虑跨站请求伪造(CSRF)攻击。¶
第 6.1.3.3 节概述了 针对 CSRF 攻击的各种缓解策略的细微差异。特别是对于 令牌中介后端,这些 CSRF 防御只适用于 应用可获取其访问令牌的一个或多个端点。¶
令牌中介后端是作为服务器端组件运行的机密客户端。 令牌中介后端可以采用 机密客户端的安全最佳实践,例如基于密钥的客户端 认证。¶
本节重新审视第 5 节中的攻击场景和后果,并讨论潜在的 额外防御。¶
如果攻击者能够在应用的执行上下文中执行恶意代码, 以下攻击场景就会变得 相关:¶
请注意,这些攻击场景会导致以下 后果:¶
将访问令牌暴露给基于浏览器的应用 是令牌中介后端架构模式背后的核心思想。 因此,访问令牌会受到恶意 基于浏览器代码的令牌窃取威胁。¶
下列其他攻击场景由令牌中介后端有效 缓解:¶
令牌中介后端通过不向基于浏览器的应用 暴露刷新令牌来抵御前两个攻击 场景。即使攻击者完全控制了应用,也根本 没有可被窃取的刷新令牌。¶
第三个场景中,攻击者通过运行静默流程获得新的 访问令牌(以及可选的刷新令牌), 通过使令牌中介后端成为机密客户端得到缓解。即使 攻击者成功获得授权码,也会因缺少客户端凭据而无法 交换此代码。此外,使用 PKCE 可防止 针对授权码的其他攻击。¶
由于令牌中介后端的性质, 潜在攻击的以下后果变得无关:¶
虽然此架构会固有地暴露访问令牌, 但仍有一些额外防御可以帮助提升应用的安全态势。¶
鉴于令牌中介后端 模式的性质,浏览器中不需要持久性令牌存储。 需要时,应用始终可以使用其基于 Cookie 的会话 从令牌中介后端获取访问令牌。第 8 节 提供了浏览器中各种存储 机制的安全属性的更多细节。¶
需要注意,即使访问令牌被 存储在恶意的基于浏览器的代码触及不到的地方,恶意代码 仍然可以模仿合法应用,并向 令牌中介后端发送请求以获取最新访问令牌。¶
在此架构中使用发送者约束访问令牌 并不简单。令牌中介后端负责 用授权码或刷新令牌交换访问 令牌,但应用会使用该访问令牌。使用 DPoP [RFC9449] 之类的机制需要在 两方之间拆分职责,而这不是 该规范定义的场景。在这种场景中使用 DPoP 超出了 本文档范围。¶
本节描述一种基于浏览器的应用架构,它 充当 OAuth 客户端,在浏览器中处理所有 OAuth 职责。因此, 基于浏览器的应用从授权服务器获取令牌,而不涉及 后端组件。¶
如果攻击者能够在浏览器中执行恶意代码,此 应用架构会受到先前讨论的所有攻击场景的影响(第 5.1 节)。本质上, 攻击者将能够从授权服务器获取访问令牌和刷新令牌, 从而可能代表用户长期访问受保护资源。¶
在此架构中,代码首先从静态 Web 主机加载到 浏览器中(A),然后应用在浏览器中运行。在此场景中, 基于浏览器的应用被视为公共 客户端,它不持有用于向授权 服务器认证的客户端凭据。¶
应用通过使用 PKCE 扩展启动授权码流程(见第 6.3.2.1 节)来获取授权码(B)。 应用使用浏览器 API (例如 [Fetch])向令牌端点(C)发出 POST 请求,以用授权码交换令牌。¶
随后,应用负责使用适当的 浏览器 API 尽可能安全地存储访问令牌和可选的刷新令牌, 如第 8 节所述。¶
当浏览器中的应用想要向 资源服务器发出请求时, 它可以直接与资源服务器交互。应用在请求中包含访问 令牌(D) 并接收资源服务器的响应(E)。¶
作为公共客户端([RFC6749] 的 第 2.1 节)并使用 [RFC6749] 的 第 4.1 节所述授权码授权类型的基于浏览器的应用,MUST 还 遵循本节描述的这些额外要求。¶
作为公共客户端的基于浏览器的应用 MUST 在获取访问令牌时实现 Proof Key for Code Exchange (PKCE [RFC7636])扩展, 并且授权服务器 MUST 支持并 对此类客户端强制执行 PKCE。¶
PKCE 扩展可以防止一种攻击:授权码被截获 并由恶意客户端交换为访问令牌。它通过向 授权服务器提供一种方式,以验证交换 授权码的客户端实例与启动该流程的客户端实例是同一个。¶
基于浏览器的应用 MUST 防止针对其重定向 URI 的 CSRF 攻击。 这可以通过以下任一方式完成:¶
对于基于浏览器的客户端,刷新令牌通常 是持有者令牌,除非应用显式使用 DPoP [RFC9449]。因此, 刷新令牌泄漏的风险 大于访问令牌泄漏,因为攻击者可能能够 继续使用被盗刷新令牌来获取新的访问令牌,并且可能 不会被 授权服务器检测到。¶
授权服务器可以选择是否向基于浏览器的 应用签发刷新令牌。然而,鉴于第三方 Cookie 阻止 机制的影响,使用刷新令牌已经变得明显更有吸引力。 [RFC9700] 在 [RFC6749] 的建议基础上,描述了围绕刷新令牌的一些 额外要求。符合此 BCP 的应用和授权 服务器如果向基于浏览器的 应用签发刷新令牌,也 MUST 遵循 [RFC9700] 中围绕刷新令牌的建议。¶
特别是,授权服务器:¶
MUST 要么在每次使用时轮换刷新令牌, 要么使用 [RFC9700] 的 第 4.14.2 节所述的发送者约束刷新令牌¶
MUST 要么为刷新令牌设置最长生命周期, 要么在刷新令牌在某段时间内未被使用时使其过期¶
在签发轮换后的刷新令牌时,如果刷新令牌具有预先设定的 过期时间,MUST NOT 将新的刷新令牌生命周期延长到 初始刷新令牌生命周期之外¶
将总体刷新令牌生命周期限制为 初始刷新令牌的生命周期,可以确保被盗刷新令牌不能 被无限期使用。¶
例如:¶
用户授权某个应用,签发一个 持续 10 分钟的访问令牌,以及一个持续 8 小时的刷新令牌¶
10 分钟后,初始访问令牌 过期,因此应用使用刷新令牌获取新的访问 令牌¶
授权服务器返回一个新的访问 令牌,持续 10 分钟,以及一个新的刷新令牌,持续 7 小时 50 分钟¶
这会持续进行,直到距离 初始授权过去 8 小时¶
此时,当应用尝试 在 8 小时后使用刷新令牌时,请求会失败, 应用必须重新发起一个依赖用户认证或此前已建立会话的授权码流程¶
授权服务器 SHOULD 将 刷新令牌的生命周期与用户在授权服务器上的已认证会话关联起来。 这样可确保当用户登出时,先前向 基于浏览器的应用签发的刷新令牌变为无效,从而模拟单点登出场景。 授权服务器 MAY 相比其他公共 客户端,为基于浏览器的应用设置不同的刷新令牌签发、 生命周期和过期策略。¶
由于基于浏览器的应用源代码会 交付到最终用户的 浏览器,因此不适合包含预置密钥。因此, 基于浏览器的应用通常按 [RFC6749] 的 第 2.1 节定义部署为公共客户端。¶
静态包含在分发给
多个用户的应用中的密钥,不应被视为机密密钥,因为某个
用户可能检查自己的副本并得知共享密钥。基于这一
原因,以及 [RFC6819] 的 第
5.3.1 节所述原因,授权
服务器 MUST NOT 要求使用共享密钥的基于浏览器
应用进行客户端认证,因为这除了
客户端标识外没有任何价值,而客户端标识已经由 client_id
参数提供。¶
仍然要求 SPA 客户端使用静态 包含的共享 密钥的授权服务器,MUST 将该客户端视为公共 客户端,并且不得接受该密钥作为客户端身份的证明。在没有 额外措施的情况下,这类客户端会受到客户端冒充的影响 (见下文第 6.3.3.2 节)。¶
如 [RFC6749] 的 第 10.2 节所述,授权 服务器 SHOULD NOT 在没有用户同意或交互的情况下自动处理授权请求, 除非授权 服务器能够确认客户端应用的身份。¶
如果授权服务器将重定向 URI 限制为一组固定的绝对
HTTPS URI,防止使用通配符域、通配符路径或通配符
查询字符串组件,
则授权服务器 MAY 接受这种已注册绝对 HTTPS URI 的精确匹配,
作为客户端身份的
证明,用于决定在
client_id
的先前请求已经被批准时,是否自动
处理授权请求。¶
在基于浏览器的应用中,常见做法是在次级窗口(例如 popup 或 iframe) 中执行 OAuth 流程,而不是重定向主窗口。 在这些流程中,基于浏览器的应用保持对主窗口的控制,例如 为了避免页面刷新,或静默运行基于 frame 的流程。¶
如果基于浏览器的应用和授权服务器 在不同 frame 中调用,它们必须使用浏览器内通信 技术,例如 postMessage API(又称 [WebMessaging]),而不是顶层 重定向。 为保证消息的机密性和真实性,postMessage 的发起方 源和接收方源都 MUST 使用 postMessage API 固有提供的机制进行验证([WebMessaging] 中的第 9.3.2 节)。¶
[RFC9700] 的 第 4.18 节提供了有关 浏览器内通信流程安全性的额外细节,以及 基于浏览器的应用和授权服务器 MUST 采用以防御 这些攻击的对策。¶
在此场景中,应用使用浏览器 API 向授权服务器和资源服务器发送请求。鉴于 OAuth 2.0 的性质,这些请求通常是跨源的,受到浏览器强制执行的 跨源通信限制。 授权服务器和资源服务器 MUST 发送必要的 CORS 标头 (定义见 [Fetch]),以 允许应用发出必要的跨源请求。请注意,在 基于浏览器的 OAuth 客户端与授权服务器或资源服务器运行在同一 源的特殊场景中,不需要 CORS 策略即可启用必要的交互。¶
对于授权服务器,CORS 配置与 令牌端点相关,基于浏览器的应用在该端点用 授权码交换令牌。此外,如果授权服务器 向应用提供额外端点,例如 discovery metadata URLs、JSON Web Key Sets、dynamic client registration、revocation、introspection 或 user info 端点,这些端点也可能由 基于浏览器的应用访问。因此,授权服务器负责 在这些端点上支持 CORS。¶
本规范不包含 决定具体 CORS 策略实现的指南,该实现可以是 通配符源或更严格的配置。请注意,CORS 有两种 操作模式,具有不同的安全属性。第一种模式适用于 CORS 安全列表请求,以前称为简单请求,在这种模式中,浏览器 发送请求,并使用 CORS 响应标头来决定响应 是否可以暴露给客户端执行上下文。对于非 CORS 安全列表 请求,例如带有自定义请求标头的请求,浏览器会先 使用预检检查 CORS 策略。只有当服务器在预检响应中给出批准时, 浏览器才会发送实际 请求。¶
请注意,由于授权服务器的具体 配置,预检的 CORS 响应可能不同于实际请求的 CORS 响应。 在预检期间, 授权服务器只能验证提供的源,但在实际 请求期间,授权服务器拥有完整请求数据,例如客户端 ID。因此,授权服务器可以在 预检期间批准已知源,但在将该源与此特定客户端的预注册源列表比较后 拒绝实际请求。¶
本节重新审视第 5 节中的攻击场景和后果,并讨论潜在的 额外防御。¶
如果攻击者能够在应用的执行上下文中执行恶意代码, 以下攻击场景就会变得 相关:¶
最危险的攻击场景是获取和 提取新令牌。在此攻击场景中,攻击者只与 授权服务器交互,这使客户端中 OAuth 功能的实际实现细节 变得无关。即使合法客户端 应用找到一种能够将令牌与攻击者完全隔离的方法,攻击者 仍然能够从授权服务器获取令牌。¶
请注意,这些攻击场景会导致以下 后果:¶
虽然此架构固有地容易受到 恶意基于浏览器代码的影响,但仍有一些额外防御可以帮助 提升应用的安全态势。请注意,这些 防御都不能处理或修复允许攻击者运行新 流程来获取令牌的根本问题。¶
直接处理令牌时,应用 可以选择不同的存储机制来存储访问令牌和 刷新令牌。普遍可访问的存储区域,例如 Local Storage [WebStorage],比更隔离的存储区域更容易被 恶意 JavaScript 访问,例如 Web Worker [WebWorker]。第 8 节 会更详细地讨论不同存储机制及其权衡。¶
一种实际实现模式可以使用 Web Worker [WebWorker] 来隔离刷新 令牌,并向应用提供用于向资源服务器发出请求的访问令牌。 这会阻止攻击者使用 应用的刷新令牌来获取新令牌。¶
然而,即使某种令牌存储机制 能够将令牌与攻击者完全隔离,也不能阻止 攻击者运行新流程来获得一组新的令牌(参见第 5.1.3 节)。¶
基于浏览器的 OAuth 客户端可以实现 DPoP [RFC9449],以 从持有者访问令牌和持有者刷新令牌转换为 发送者约束令牌。在这种实现中,用于签名 DPoP 证明的私钥 由浏览器处理(一个不可提取的 [CryptoKeyPair] 使用 [W3C.IndexedDB] 存储)。因此, 使用 DPoP 可以有效防止 XSS 攻击者 外泄应用令牌的场景(参见第 5.1.1 节和第 5.1.2 节)。¶
请注意,使用 DPoP 不会阻止 攻击者运行新流程来获得新的访问令牌(以及 可选的刷新令牌)第 5.1.3 节。即使 DPoP 是 强制的,攻击者也可以将新的令牌集绑定到其控制下的密钥对, 从而允许他们外泄发送者约束 令牌,并通过依赖攻击者控制的密钥来 计算必要的 DPoP 证明以使用这些令牌。¶
客户端应用和后端应用在过去 二十年中已经显著演进,威胁、攻击者模型以及对现代应用 安全性的总体理解也随之发展。因此,业内通常接受的既往建议以及 OAuth 工作组发布的建议往往不再被推荐,而提出的解决方案也经常无法 满足预期的安全要求。¶
本节讨论几种替代架构模式,这些模式不 推荐用于现代基于浏览器的 OAuth 应用。本节会讨论每种 模式,并在相关时附带威胁分析,以考察攻击场景和 后果。¶
很多时候,简单应用会因为使用 OAuth 来替代会话管理概念而变得不必要地复杂。一个典型示例是 服务器端 MVC 应用的现代形态,它现在由基于浏览器的前端和 服务器端 API 组成。¶
在这样的应用中,使用 OpenID Connect 将用户 认证卸载给专用提供方,可以显著简化应用的架构 和开发。然而,使用 OAuth 来管理前端与 后端之间的访问通常并非必要。应用可以不使用访问令牌,而依赖 传统的基于 Cookie 的会话状态来跟踪用户的认证状态。 保护会话 Cookie 的安全指南在第 6.1.3.2 节中讨论。¶
虽然在本文档中建议不使用 OAuth 似乎不太合适,但 需要注意的是,OAuth 最初是为第三方或联合式 API 访问而创建的, 因此在单一公共域部署中,它可能不是最佳解决方案。话虽如此, 即使在公共域架构中,使用 OAuth 仍然有一些优势:¶
为未来提供更多灵活性,例如之后要向系统中 添加一个新域。如果 OAuth 已经就绪,添加新域 不需要任何额外的架构重构。¶
能够利用现有库支持,而不是 为集成编写定制代码。¶
在 OAuth 服务器集中处理登录和多因素认证支持、 账户管理和恢复,而不是使其成为 应用逻辑的一部分。¶
在认证用户和 提供资源之间划分职责¶
在第一方同域 场景中为基于浏览器的应用使用 OAuth 可以提供这些优势,并且可以通过上述任何架构模式 实现。¶
OAuth 2.0 隐式授权类型(定义见 [RFC6749] 的 第 4.2 节) 的工作方式是:授权服务器在 授权响应(前端通道)中签发访问令牌,而没有授权码交换步骤。在这种 情况下,访问 令牌会在重定向 URI 的片段部分返回,这为攻击者 拦截和窃取访问令牌提供了多种机会。¶
隐式授权类型的安全属性使其不再是 推荐的最佳实践。为了有效防止使用此流程,授权服务器 MUST NOT 在授权响应中签发访问令牌,并且 MUST 只从 令牌端点签发访问令牌。基于浏览器的客户端 MUST 使用授权 码授权类型,并且 MUST NOT 使用隐式授权类型来获取访问令牌。¶
从历史上看,隐式授权类型为 基于浏览器的应用提供了一个优势,因为 JavaScript 始终可以任意读取和操作 URL 的片段部分, 而不会触发页面重新加载。为了在应用获得 访问令牌后从 URL 中移除它,这是必要的。此外,在 CORS 在浏览器中广泛普及之前,隐式授权类型 提供了一种不要求浏览器或服务器支持 CORS 的替代流程。¶
现代浏览器现在拥有 Session History API(见 [HTML] 中的 “Session history and navigation”),它提供了一种无需触发页面重新加载即可修改 URL 的路径和查询字符串 组件的机制。此外,CORS 已得到广泛 支持,并且单页应用经常将其用于许多目的。这意味着 现代基于浏览器的应用可以 使用带有 PKCE 的 OAuth 2.0 授权码授权类型,因为借助 Session History API,它们能够 在不触发页面重新加载的情况下 从查询字符串中移除授权码,并且令牌端点的 CORS 支持意味着即使授权服务器位于不同域, 应用也可以获取令牌。¶
除了已经讨论过的攻击场景和后果外, 还有一些额外攻击进一步支持弃用 隐式授权类型。[RFC6819] 和 [RFC9700] 的 第 4.1.2 节 所描述的许多针对隐式授权类型的攻击 没有足够的缓解策略。以下各节描述了在继续使用隐式授权类型的情况下 无法缓解的具体 攻击。¶
如果攻击者能够使授权响应 被发送到其控制下的 URI,他们就会直接获得对授权响应的访问, 包括访问令牌。 [RFC9700] 中详细描述了执行此攻击的几种方法。¶
攻击者可以从 浏览器历史记录中获取访问令牌。 [RFC6819] 推荐的对策仅限于对令牌使用较短的 过期 时间,并指示浏览器不应缓存响应。 这些都不能完全防止此攻击,它们只能减少潜在 损害。¶
此外,许多浏览器现在还会将浏览器历史记录同步 到云服务和 多个设备,从而提供更广泛的攻击面,以便从 URL 中提取访问 令牌。¶
攻击者可以通过各种 方式修改页面或向浏览器注入脚本,包括浏览器的 HTTPS 连接被拦截时, 例如被企业网络拦截。 虽然针对 TLS 层的攻击通常超出 基础安全建议的防护范围,但对于基于浏览器的 应用,这类攻击 更容易执行。注入的脚本可以使攻击者访问 页面上的所有内容。¶
当应用使用一种已知的标准方式获取访问令牌时,
页面上运行恶意脚本的风险可能会被
放大,也就是说攻击者
始终可以查看 window.location 变量来查找访问
令牌。这种威胁画像
不同于攻击者通过知道经授权码流程获得的访问令牌可能
存储在何处或如何存储,从而专门针对单个应用发起攻击。¶
在基于浏览器的应用中使用第三方脚本相当常见,例如 分析工具、崩溃报告,甚至诸如社交媒体“like” 按钮之类的东西。 在这些情况下,应用作者可能无法完全 了解应用中运行的全部代码。当访问令牌在 片段中返回时,页面上的任何第三方脚本都可以看到它。¶
资源所有者密码凭据授权 MUST NOT 被使用,如 [RFC9700] 的 第 2.4 节所述。 相反,使用授权 码授权类型并将用户重定向到授权服务器, 为授权服务器提供了机会,以提示用户使用 安全且不可钓鱼的认证选项、利用单点登录会话, 或使用第三方身份提供方。相比之下,资源所有者密码凭据 授权没有 为这些能力提供任何内置机制,而必须使用自定义 协议进行扩展。¶
为了符合此最佳实践,使用 OAuth 或 OpenID Connect 的基于浏览器的应用 MUST 使用基于重定向的流程(例如 OAuth 授权码授权类型), 如本文档所述。¶
为了限制攻击者提取现有令牌或 获取一组新令牌的能力,过去曾有人建议使用 Service Worker([W3C.service-workers])的模式。 在此模式中,应用加载后的第一个动作是 注册 Service Worker。Service Worker 负责执行 授权码流程以获取令牌,并使用合适的访问令牌增强发往资源服务器的传出请求。 此外,Service Worker 会阻止客户端应用的 代码直接调用授权服务器端点。此限制旨在 针对“新令牌的获取和提取”攻击场景(第 5.1.3 节)。¶
下面包含的时序图说明了 客户端、Service Worker、授权服务器和资源服务器之间的交互。¶
请注意,此模式从不将令牌暴露给运行在 浏览器中的应用。由于 Service Worker 在隔离的执行环境中运行,因此没有 共享内存,也没有办法让客户端应用影响 Service Worker 的执行。¶
本节讨论的架构模式容易受到 以下攻击场景影响:¶
因此,此模式可能导致以下后果:¶
使用 Service Worker 看似有前景的安全收益 值得更详细地讨论其安全限制。 为了针对相关攻击场景(第 5.1 节)充分保护应用, Service Worker 需要满足两个安全要求:¶
一旦注册,Service Worker 就会运行授权 码流程并获取令牌。由于 Service Worker 在自己的隔离执行环境中跟踪令牌, 它们无法被任何 应用代码触及,包括潜在的恶意代码。因此, Service Worker 满足了防止令牌外泄的第一项要求。 这实际上中和了第 5.1 节中讨论的前两个攻击场景。¶
为了满足第二项安全要求,Service Worker 必须能够保证控制合法 应用的攻击者不能执行新的授权码授权,这是 第 5.1.3 节中讨论的攻击。 由于 Service Worker 的性质,已注册的 Service Worker 能够 阻止所有发起此类新流程的传出请求,即使这些请求 发生在 frame 或新窗口中。¶
然而,在应用内部运行的恶意代码 可以注销此 Service Worker。注销 Service Worker 可能会对应用产生 显著功能影响,因此浏览器不会轻率处理此操作。 因此,被注销的 Service Worker 会被标记为 这种状态,但所有当前运行的实例仍保持活动,直到其 对应的浏览上下文被终止(例如通过关闭标签页或 窗口)。所以即使攻击者注销了 Service Worker,它仍然保持 活动,并能够防止攻击者到达授权 服务器。¶
注销 Service Worker 的一个后果是, 当打开新的浏览上下文时,它将不存在。因此,当 攻击者先注销 Service Worker,然后在 frame 中启动新流程时,frame 的浏览上下文将没有关联的 Service Worker。 因此,攻击者将能够运行自己的新 授权码授权,从 frame 的 URL 中提取授权码, 并将其交换为令牌。本质上,Service Worker 未能满足 第二项安全要求,使其容易受到攻击者获取一组新令牌的场景影响 (第 5.1.3 节)。¶
由于这些缺陷,再加上注册和维护 Service Worker 的显著 复杂性,不推荐此模式。¶
最后,请注意,单独使用 Service Worker 不会增加应用的攻击面。实践中,Service Worker 常被用于为旧版应用改造支持,以便在传出请求中包含 OAuth 访问令牌。 在这些场景中,Service Worker 不会改变应用的安全属性,而只是 简化应用的开发和维护。¶
当基于浏览器的应用直接处理 OAuth 访问令牌或刷新令牌时, 它就需要负责临时或持久地存储这些令牌。因此, 应用需要决定如何管理令牌(例如,内存存储与持久存储),以及采取哪些 步骤来进一步将令牌与主应用代码隔离。本节讨论几种 不同存储机制及其属性。这些建议考虑了 OAuth 令牌的独特 属性,其中一些可能与通用浏览器安全建议重叠。¶
在讨论基于浏览器的令牌存储解决方案的安全属性时, 需要理解攻击者在攻陷基于浏览器的应用时具备的能力。 与前面的讨论类似,应考虑两个主要攻击场景:¶
由于攻击者的代码会变得与合法应用的 代码无法区分,攻击者始终能够以与 合法应用代码完全相同的方式向提供方请求令牌。因此,即使是完全隔离的令牌存储解决方案,也无法 解决第二种威胁的危险,即攻击者向提供方请求令牌。¶
话虽如此,基于浏览器的存储解决方案的不同安全属性 会影响攻击者从存储中获取现有令牌的能力。¶
Service Worker([W3C.service-workers])提供了一个完全隔离的环境 来跟踪令牌。这些令牌无法被客户端应用访问,从而有效 防止其被外泄。为了保证这些令牌的安全性,Service Worker 不能与应用共享这些令牌。因此,每当应用 想要执行依赖令牌的操作时,都必须请求 Service Worker 执行该 操作并返回结果。¶
当目标是将令牌与应用执行上下文隔离时, Service Worker MUST NOT 将令牌存储在任何与主 窗口共享的持久存储 API 中。例如,目前 IndexedDB 存储在浏览上下文和 Service Worker 之间共享,因此它并不适合让 Service Worker 持久化那些应 保持无法被主窗口访问的数据。因此,Service Worker 目前 无法访问隔离的持久存储区域。¶
如前所述,使用 Service Worker 并不能防止攻击者 获取一组新的令牌。同样,如果应用负责从授权服务器获取 令牌并将其传递给 Service Worker 进一步 管理,攻击者也可以执行与合法应用相同的操作来获取 这些令牌。¶
应用可以使用 Web Worker [WebWorker],这会产生与前一种依赖 Service Worker 的场景几乎相同的 场景。Service Worker 与 Web Worker 的区别在于访问级别及其运行时属性。Service Worker 可以拦截 并修改传出请求,而 Web Worker 只是运行后台任务的一种方式。Web Worker 是短暂的,会在浏览上下文关闭时消失,而 Service Worker 是注册在浏览器中的持久服务。¶
使用 Web Worker 的安全属性与使用 Service Worker 相同。当令牌暴露给应用时,它们就变得易受攻击。当需要使用令牌时, 依赖令牌的操作必须由 Web Worker 执行。¶
隔离刷新令牌的一种常见方法是使用 Web Worker。在这种 场景中,应用从 Web Worker 启动授权码流程。 重定向中的授权码被转发给 Web Worker,随后 Web Worker 将其交换为 令牌。Web Worker 将刷新令牌保存在内存中,并将访问令牌发送给主 应用。主应用按需使用访问令牌。当应用需要 运行刷新令牌流程时,它会请求 Web Worker 执行,之后应用 获取新的访问令牌。¶
在此场景中,应用自身的刷新令牌可以有效防止 外泄,但访问令牌不能。此外,没有什么可以阻止 攻击者通过运行新的授权码流程获取自己的令牌 第 5.1.3 节。¶
另一种选择是将令牌保存在内存中,而不使用任何持久 存储。这样做会将令牌的暴露限制在当前执行上下文内,但 缺点是无法在页面加载之间持久保存令牌。¶
在 JavaScript 执行环境中,可以通过使用闭包变量 有效屏蔽令牌的直接 访问,从而进一步增强内存中令牌存储的安全性。通过使用闭包,令牌只 可由闭包内预定义的函数访问,例如用于向资源服务器发起请求的函数。¶
虽然闭包在简单、隔离的环境中效果很好,但在像浏览器执行环境这样的复杂环境中,
它们很难确保安全。例如,闭包
依赖各种外部函数来执行其操作,例如
toString 函数或网络 API。通过原型投毒,攻击者可以
将这些函数替换为恶意版本,从而导致闭包后续操作
使用这些恶意版本。在恶意函数内部,攻击者可以访问
函数参数,这可能会将闭包内部的令牌暴露给攻击者。¶
截至本文撰写时,浏览器中当前可用的持久存储 API 是 localStorage([WebStorage])、 sessionStorage([WebStorage])以及 [W3C.IndexedDB]。¶
localStorage 会在页面重新加载之间持久存在,并在所有 标签页之间共享。此存储可由整个源访问,并且更长期地持久存在。localStorage 不能防止恶意 JavaScript 的未授权访问,因为攻击者会在 同一源内运行代码,因此能够读取 localStorage 的内容。此外,localStorage 是同步 API,会阻塞其他 JavaScript,直到 操作完成。¶
sessionStorage 类似于 localStorage,只是 sessionStorage 的生命周期与浏览器标签页的生命周期相关。此外,sessionStorage 不会 在打开到同一源页面的多个标签页之间共享,这会稍微减少 sessionStorage 中令牌的暴露。¶
IndexedDB 是一种类似 localStorage 的持久存储机制,但它 在多个标签页之间以及浏览上下文和 Service Worker 之间共享。此外, IndexedDB 是异步 API,相比同步的 localStorage API 更受偏好。¶
请注意,这些模式之间的主要区别在于 数据暴露程度,但当攻击者能够在应用执行环境中 执行恶意代码时,这些选项都无法完全缓解令牌外泄。¶
在所有情况下,截至本文撰写时,都不能保证浏览器存储 在静态状态下已加密。此行为可能会将令牌暴露给 有能力读取磁盘文件的攻击者。虽然此类攻击依赖的能力远超 基于浏览器的应用范围,但此主题突出展示了针对 现代应用的重要攻击向量。越来越多恶意软件专门被创建出来,用于爬取用户机器, 寻找浏览器配置文件以获取高价值令牌和会话 Cookie,从而导致 账户接管攻击。¶
虽然基于浏览器的应用无法缓解此类攻击, 但应用可以通过使用加密确保数据机密性来缓解此类攻击的后果。 [W3C.WebCryptoAPI] 为 JavaScript 代码提供了一种 生成密钥的机制,并提供使该密钥不可导出的选项。JavaScript 应用随后可以使用此 API 在存储令牌之前对其进行加密和解密。然而, [W3C.WebCryptoAPI] 规范只确保密钥不可导出给浏览器代码,但没有 对密钥本身在操作系统中的底层存储提出任何要求。因此, 不能依赖不可导出密钥作为防止从底层文件系统 外泄的方式。¶
为了防止令牌从文件系统外泄, 加密密钥需要存储在文件系统以外的位置,例如远程 服务器。这为纯基于浏览器的应用引入了新的复杂性,并且超出了 本文档范围。¶
如本文档通篇所讨论,使用发送者约束令牌 并不能解决仅浏览器 OAuth 客户端的安全限制。然而,当 令牌中介后端(第 6.2 节)或仅浏览器 OAuth 客户端(第 6.3 节)提供的安全级别足以满足 当前用例时,可以使用发送者约束令牌来增强访问 令牌和刷新令牌的安全性。一种以可供基于浏览器的应用使用的方式实现发送者约束令牌的方法是 DPoP [RFC9449]。¶
使用发送者约束令牌时,OAuth 客户端必须证明拥有 私钥才能使用令牌,从而使令牌本身不可单独使用。如果 发送者约束令牌被盗,攻击者无法直接使用该令牌, 他们还需要窃取私钥。本质上,可以说使用 发送者约束令牌将安全存储令牌的挑战转移为安全存储 私钥。理想情况下,应用应使用不可导出的私钥,例如 使用 [W3C.WebCryptoAPI] 生成的私钥。当浏览器 存储中的未加密令牌受到不可导出私钥保护时,XSS 攻击将无法提取 该密钥,因此攻击者无法使用该令牌。¶
如果应用无法使用生成不可导出 密钥的 API,应用应采取措施将私钥与自身执行 上下文隔离。执行这一点的技术类似于使用安全令牌存储机制,如 第 8 节所讨论。¶
虽然不可导出密钥在 JavaScript 上下文中受到保护,不会被 外泄,但从文件系统外泄底层私钥仍然 是一个潜在攻击向量。在撰写本文时,[W3C.WebCryptoAPI] 并不保证 不可导出密钥确实受到可信平台模块(TPM)保护,或以 加密形式存储在磁盘上。如果攻击者能够访问用户机器的文件系统, 例如通过恶意软件, 从底层文件系统外泄不可导出密钥仍可能发生。 这实际上使潜在攻击向量等同于 会话劫持攻击。¶
授权服务器混淆攻击对于每个支持 至少两个授权服务器的客户端都是严重威胁。[RFC9700] 的 第 4.4 节提供了关于 混淆攻击 以及上述对策的更多细节。¶
[[ 将从最终规范中移除 ]]¶
-26¶
将 Cookie 属性改为等宽格式¶
-25¶
对“browser-based application”使用一致术语,并且仅在明确需要时使用 JavaScript¶
将“hard drive”替换为“local persistent storage”¶
添加关于 BFF 模式运维注意事项的说明¶
使用“Forwarding”而不是“Proxying”,以避免与 HTTP 代理混淆¶
少量编辑性修正¶
在首次使用时添加更多术语引用¶
添加 Session Fixation 的引用¶
-24¶
-23¶
确保缩略语和其他规范在 首次使用时得到定义和引用,并添加到术语中¶
澄清恶意 JavaScript 是文档前面威胁分析的基础¶
澄清为什么私钥的文件系统存储是一个问题¶
澄清引言中的 JS 运行时¶
处理 secdir 审查反馈¶
澄清所描述的具体攻击是与本文档相关的攻击,因为它们是 OAuth 特定的¶
描述与会话固定攻击的关系¶
澄清第 8 节专门讨论 OAuth 令牌¶
提到 localStorage 是同步的¶
应用 Martin Thompson 审查中关于恶意 JS 代码范围的建议¶
澄清“攻击 service worker”,明确这是关于 授权码流程¶
澄清在 web worker 中存储刷新令牌的意图¶
在每节首次使用时明确提到访问令牌和刷新令牌,而不是“set of tokens”¶
略微重写 Web Worker 一节,使其听起来不像推荐¶
编辑性修改,移除短语“perfect storage mechanism”¶
修正引用¶
处理 genart、opsdir、artart、secdir 和 httpdir 审查中的所有反馈¶
-22¶
-21¶
-20¶
处理 Rifaat 的审查意见(2024-11-13 电子邮件)¶
-19¶
-18¶
处理 Justin Richer 的 last call 评论¶
更新 Token-Mediating Backend 模式优势的描述¶
在 HTML 版本中添加 SVG 图¶
添加 BFF 模式的隐私注意事项¶
一致使用“grant type”、“grant”和“flow”¶
-17¶
添加关于防伪/双重提交 Cookie 作为另一种 CSRF 保护形式的一节¶
更新 CORS 术语¶
移动关于浏览器内流程的新章节,因为它不适用于 BFF 或 TM 模式¶
修正某些浏览器技术术语的使用¶
编辑性改进¶
-16¶
-15¶
-14¶
-13¶
-12¶
修订概述和服务器支持清单,使其与草案其余部分保持最新¶
添加关于令牌存储选项的新章节¶
添加关于发送者约束令牌的一节以及 DPoP 引用¶
重写架构模式,使其聚焦于令牌获取¶
添加一节,讨论为什么不应使用 Cookie API 存储令牌¶
-11¶
-10¶
-09¶
-08¶
添加说明:除 SameSite 等之外还要使用“Secure”Cookie 属性¶
更新以使此草案与最新 Security BCP 同步¶
更新 mix-up 对策文本,以引用新的“iss” 扩展¶
将刷新令牌轮换的“SHOULD”改为 MUST,要么使用轮换,要么 使用发送者约束,以匹配 Security BCP¶
修正对其他规范和扩展的引用¶
改进不同架构描述中的编辑内容¶
-07¶
-06¶
-05¶
-04¶
禁止使用 Password Grant¶
在授权服务器要求总结列表中添加 PKCE 支持¶
重写刷新令牌一节,以允许在刷新令牌有时间限制、每次使用时轮换, 并要求轮换后的刷新令牌生命周期不 超过初始刷新令牌生命周期时使用刷新令牌,同时使其与 Security BCP 保持一致¶
更新关于使用 state 的建议,以反映 Security BCP¶
更新服务器支持清单以反映最新变更¶
更新同域 JS 架构一节,以强调架构而不是域¶
对讨论 OpenID Connect ID token 的章节进行编辑性澄清¶
-03¶
更新关于片段 URL 的历史说明,澄清 Session History API 意味着浏览器可以使用未修改的授权码授权类型¶
重写“Authorization Code grant type”引言段落,以更好引出 接下来的两个章节¶
将公共域部署中“is likely a better decision to avoid using OAuth entirely”软化为 “it may be...”¶
更新摘要,使其不再局限于公共客户端,因为后续 章节讨论了机密客户端¶
移除关于同域 架构中避免使用 OpenID Connect 的引用¶
更新标题以更好描述架构(Applications Served from a Static Web Server -> JavaScript Applications without a Backend)¶
扩展“same-domain architecture”章节,以更好解释 OAuth 在此场景中的问题¶
在可能处引用 Security BCP 来说明隐式授权类型攻击¶
少量拼写修正¶
-02¶
纳入 Leo Tohill 的反馈重写概述章节¶
更新总结推荐要点,将应用和 服务器要求分开¶
移除仅主机名重定向 URI 匹配的允许项,现在 要求精确重定向 URI 匹配¶
更新第 6.2 节,删除带后端组件的 SPA 是公共客户端的引用¶
扩展架构章节,明确提到 JS 应用可用的三种 架构模式¶
-01¶
作者感谢 William Denniss 和 John Bradley 的工作, 他们针对原生应用的建议为 基于浏览器的应用的许多最佳实践提供了依据。作者还感谢 Hannes Tschofenig 和 Torsten Lodderstedt,感谢 Internet Identity Workshop 27 会议中最初提出此 BCP 的那场会议的与会者,以及以下 贡献了思想、反馈和措辞并塑造和形成最终规范的个人:¶
Andy Barlow, Andy Newton, Annabelle Backman, Brian Campbell, Brock Allen, Christian Mainka, Damien Bowden, Daniel Fett, Deb Cooley, Elar Lang, Emmanuel Gautier, Eric Vyncke, Erik Kline, Eva Sarafianou, Filip Skokan, George Fletcher, Hannes Tschofenig, Janak Amarasena, John Bradley, Joseph Heenan, Justin Richer, Karl McGuinness, Karsten Meyer zu Selhausen, Leo Tohill, Louis Jannett, Marc Blanchet, Martin Thomson, Matthew Bocci, Mike Bishop, Mike Jones, Mohamed Boucadair, Orie Steele, Qin Wu, Rifaat Shekh-Yusef, Roman Danyliw, Sean Kelleher, Thomas Broyer, Thomas Fossati, Tomek Stojecki, Torsten Lodderstedt, Vittorio Bertocci, Watson Ladd, William Duncan, and Yannick Majoros.¶