A field guide · RFC 9700 / BCP 240

一张通行证的旅程

用一个故事读懂 OAuth 2.0 安全最佳实践:谁发起、交给谁、从哪里返回,以及一张令牌究竟能打开哪扇门。

01 / A small fable

城里的信使与通行证

一座城里有座档案馆。居民阿林想请一位信使,代她去取一本指定的册子。她不愿把家门钥匙交给信使,于是来到城中的签发处,请签发人另开一张有范围、有期限的通行证。

最初,签发人只问信使:“办好之后送到哪里?”有人趁乱改了地址,信件便被送进一条陌生小巷。后来,签发处只认事先登记的收件地址,也不允许登记过的门牌再把信随便转走。

接着,又有人捡到半路遗失的取件凭条,跑去冒领。签发人想了个办法:信使出发前先留下一道只能由他解开的印记;回来取证时,要证明自己知道对应的秘密。凭条即使被别人捡到,也不能单独换到通行证。

城里还有好几家签发处。于是信使在出发时记下自己选择的是哪一家;回来时不能因为门牌相似,就把某一家的凭条交给另一家。档案馆也只接受写给自己的通行证,而且只开放证上写明的册子。

这时阿林才看明白:安全不靠一张看上去精美的纸,而靠每一步都能回答“这是谁发起的、由谁签发、交到哪里、能做什么”。这就是我们理解 OAuth 授权安全的起点。

故事是理解工具,不是协议流程或密码学描述。PKCE 的秘密由客户端生成和保存,授权服务器验证证明;它不是用户密码,也不代替其他安全检查。

02 / Translation

故事里的人分别是谁?

故事OAuth 中的角色 / 事物一句话
阿林资源所有者 Resource Owner有权对受保护资源授权的人。
信使客户端 Client请求授权并调用资源的应用;不是 OAuth 协议中的浏览器用户本人。
签发处授权服务器 Authorization Server认证用户、获取适用授权并签发令牌。
档案馆资源服务器 Resource Server持有受保护 API / 资源,验证令牌并执行权限判断。
取件凭条授权码 Authorization Code短期的一次性中间凭证,用于向令牌端点兑换;不是访问令牌。
通行证访问令牌 Access Token用于访问资源;并不必然是 JWT,也不应当成用户身份的通用证明。
印记与秘密PKCE challenge / verifier把授权码兑换与发起该次请求的客户端事务绑定。

这里还有一个容易混淆的点:OAuth 主要解决授权,OpenID Connect(OIDC)在 OAuth 上定义用户认证。 企业登录时的平台会校验 OIDC 身份结果;拿到 access token 本身不等于已经正确认证用户。

03 / Follow the flow

一趟授权是怎么走完的?

① 发起客户端准备 state、PKCE,并记录预期签发方;OIDC 再准备 nonce。
→
② 认证与授权浏览器到授权服务器;用户按该服务的策略完成认证与授权。
→
③ 返回授权服务器将授权码发回事先登记且匹配的回调地址。
→
④ 兑换客户端在令牌端点提交授权码与 verifier;服务端验证后发令牌。
→
⑤ 访问客户端调用 API;资源服务器检查令牌、受众和权限。
理解重点:state 负责绑定请求与浏览器事务并可用于防 CSRF;PKCE 把授权码与客户端事务绑定;OIDC 的 nonce 与 ID Token 的认证事务相关。它们用途相关但并不随意互换,必须按 RFC 所列条件使用。
04 / The practical rules

把要求讲成人话

标签保留原文的强度:MUST 为必须;SHOULD 为建议,偏离时需要理解并评估后果。下表是学习摘要,不是完整一致性声明。

规则为什么 / 怎么做规范强度与出处
回调地址精确匹配授权服务器逐字符匹配预注册的 redirect URI;不要用宽松通配或前缀匹配。MUST §2.1;原生应用 localhost 回环端口有例外。
拒绝开放重定向客户端和授权服务器都不能提供任意 URL 跳转器;可信回调后再跳到攻击者地址仍会泄漏凭证。MUST NOT §4.11
授权码 + PKCE每次生成新的 verifier,发送派生的 challenge;兑换时服务器校验 verifier。优先 S256,避免降级。公开客户端 MUST;机密客户端 SHOULD;授权服务器必须支持 PKCE。§2.1.1、§4.8
防 CSRF 与回调串线将一次性请求上下文绑定浏览器;核对 state;OIDC 校验 nonce。确认 PKCE 条件满足时可依规范使用其 CSRF 防护。客户端防 CSRF MUST;具体机制取决于条件。§4.7
多 IdP 防混淆发起时绑定预期授权服务器;按支持情况验证授权响应的发行方,或按规范使用区分回调地址等对策。多授权服务器客户端 REQUIRED;单独保存 URL 不充分。§4.4
减少令牌暴露优先授权码流程;不要把 access token 放 URL 查询参数;避免日志、浏览器历史和 Referer 泄漏。隐式授权 SHOULD NOT,除非满足文中例外条件;查询参数 MUST NOT。§2.1.2、§4.3
收紧权限与受众令牌只给必要资源和动作;API 检查令牌是否发给自己并检查对应权限。最小权限、受众限制 SHOULD;不适用该资源的令牌必须拒绝。§2.3
防令牌重放考虑通过 DPoP 或 mTLS 将 access token 绑定发送方。令牌和密钥同时被窃时保护会减弱。发送方约束 SHOULD。§2.2
保护刷新令牌先评估是否签发;公开客户端使用发送方约束或轮换。旧令牌再次使用时,轮换机制检测重放并撤销活动令牌。公开客户端二选一 MUST;服务端通常无法判断谁是攻击者。§4.14
不要让客户端收用户密码禁止 Resource Owner Password Credentials 授权方式;用户凭证只应交给可信的认证方。MUST NOT §2.4
保护客户端与传输可安全保存凭据时进行客户端认证;优先考虑非对称方式。全链路保护 TLS;反向代理不得信任外部伪造的安全头。客户端认证与非对称方式为建议;代理至应用链路须防窃听、注入和重放。§2.5、§4.13
谨慎处理 POST 后重定向带有用户凭据的请求不能用 307 跳转,以免浏览器把原 POST 内容转给客户端;若用 HTTP 跳转,推荐 303。MUST NOT / SHOULD §4.12
05 / Who does what

谁来负责,怎么验证?

责任方关键动作最小验证例子
客户端绑定事务、正确使用 PKCE、保护 state / nonce、核对预期发行方、避免泄漏。替换 state、verifier、授权码或 IdP;回调必须拒绝且不建立会话。
授权服务器校验精确回调、执行 PKCE、签发最小权限令牌、保护刷新令牌。尝试相似回调地址、错误 verifier、旧刷新令牌;均按契约拒绝或触发撤销。
资源服务器验证令牌有效性、适用受众和操作范围;保护令牌。用面向 API A 的令牌访问 API B,或用只读令牌写入;必须拒绝。
运维与平台限制日志敏感数据,保护 TLS 与代理信任边界,监控重放和异常。检查请求日志、错误页、浏览器跳转和代理头;确认不暴露 code、令牌与凭据。

这些是建议的验收例子,不能单独证明符合 RFC。具体测试还要覆盖产品使用的客户端类型、协议扩展和部署环境。

06 / Apply it here

放到企业联合登录里

员工选择“用企业账号登录”时,企业 IdP 是上游授权服务器 / OIDC Provider,本平台 auth 是客户端 / Relying Party。平台若还给下游应用发令牌,平台在下游链路中又是授权服务器。两段关系要分别配置、检查和审计。

例子:林从星河科技的入口开始登录。auth 将本次请求绑定到星河的可信 IdP、state、nonce 和 PKCE 上下文。回调后先防止发行方混淆,再按 OIDC 校验 ID Token 的签名、issuer、audience、时效及 nonce,依据可信 issuer 与稳定主体识别平台账号。随后仍需独立判断邀请、企业成员资格和角色。

这里涉及的 OIDC ID Token 校验与身份映射是企业登录需要的补充,不全是 RFC 9700 单独定义的内容。连接配置、域名发现、账号关联、会话与成员资格也需要业务规则。当前项目的企业联邦登录仍是设计方向,不能把本文当作已实现或已通过验收。

补充阅读:OpenID Connect Core:ID Token 验证。

07 / In your own words

如果要对别人复述

“OAuth 安全不是只要加一个 PKCE 就完成了。我们要先确保授权结果只回到登记过的地址,再把这次授权请求、浏览器和授权码兑换绑定起来。接多个企业 IdP 时,还要确认结果来自一开始选中的那一家。

拿到令牌以后,资源服务仍要检查令牌是不是发给自己的、允许访问哪些资源。对长期有效的刷新令牌,要考虑绑定发送方或轮换,发现重放时及时终止。每一段都做好,才不会让一个环节失守变成全链路失守。”

三句自测

  1. 攻击者拿到授权码后,为什么只有授权码通常不够?PKCE 又不能解决什么?
  2. 用星河 IdP 发起的登录,如果回调混入另一家 IdP 的结果,客户端如何识别并拒绝?
  3. 一张发给 API A 的只读令牌,在 API B 或写入操作上是否应该生效?由谁负责拒绝?
展开参考答案

① 授权码与 verifier 绑定;PKCE 不取代回调校验、令牌保护和资源授权。② 按规范绑定预期发行方并采取多授权服务器混淆防护;OIDC 的发行方与令牌也需正确校验。③ 不应生效;资源服务器验证受众与操作权限并拒绝。

08 / Read the standard

回到原文

本指南依据 2025 年 1 月发布的 RFC 9700(BCP 240)编写,采用讲解和选要方式,不覆盖所有攻击与例外。实现时应结合原文、协议扩展和安全评审。