惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
NISL@THU
NISL@THU
E
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
K
Kaspersky official blog
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
S
Schneier on Security
G
GRAHAM CLULEY
The Hacker News
The Hacker News
T
Threat Research - Cisco Blogs
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyberwarzone
Cyberwarzone
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
AWS News Blog
AWS News Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
爱范儿
爱范儿
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
S
Securelist
G
Google Developers Blog
The Last Watchdog
The Last Watchdog
Google Online Security Blog
Google Online Security Blog
美团技术团队
F
Fortinet All Blogs
小众软件
小众软件
Recorded Future
Recorded Future
V
Visual Studio Blog
B
Blog RSS Feed
H
Help Net Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
Latest news
Latest news
Spread Privacy
Spread Privacy
H
Heimdal Security Blog

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】API 网关设计:入口层的职责边界 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 排序基准测试:用数据说话
【身份与访问控制工程】OAuth 2.1 与 PKCE:现代授权主路径
Liao Tonglang · 2026-04-21 · via 土法炼钢兴趣小组的算法知识备份

OAuth 2.0(RFC 6749)发布于 2012 年。此后十二年间,安全研究发现了一系列攻击向量——授权码拦截、CSRF 状态泄露、Refresh Token 窃取——每次发现都催生一个补充 RFC。到 2024 年,OAuth 2.1 将这些安全增强整合为一份连贯规范(draft-ietf-oauth-v2-1-11),做了一次”OAuth 2.0 安全最佳实践的底线收敛”。

对工程团队来说,理解 OAuth 2.1 意味着把十几份分散的 RFC(PKCE 的 RFC 7636、Token Binding 的 RFC 8705、Refresh Token Rotation 的 RFC 7009 等)合并成一套统一的安全配置。本文以”攻击→防御”为主线,拆解 OAuth 2.1 的精简授权码流程全栈。

OAuth 2.1 最直观的变化是删除

已删除 原因 替代方案
Implicit Grant (response_type=token) access_token 直接暴露在 URL fragment 中,可被 Referer Header、浏览器历史、JavaScript 读取 Authorization Code + PKCE
Resource Owner Password Grant (grant_type=password) 用户凭据直接交给第三方应用,违反”不共享密码”原则 Authorization Code + PKCE,或 Client Credentials(服务间)
Bearer Token 在 URL query string 中的传输 URL 出现在 browser history、Referer Header、代理日志中 Authorization Header (Bearer + token) 或 POST body (application/x-www-form-urlencoded)

这意味着 OAuth 2.1 的唯一推荐授权流程是 Authorization Code Grant + PKCE。对于服务间通信,保留 Client Credentials Grant。对于设备(如智能电视、IoT),保留 Device Authorization Grant(RFC 8628)。

二、PKCE 不是可选的

PKCE(Proof Key for Code Exchange,RFC 7636)的原始动机是解决公开客户端(如移动 App)的授权码拦截问题。OAuth 2.1 将其要求扩展到所有客户端——包括机密客户端(后端服务器)。

2.1 攻击模型:为什么没有 PKCE 会出事

用户在浏览器上授权后,OP 将 authorization_code 附加在 redirect_uri 上返回给 RP。问题在于:code 经过浏览器地址栏——攻击者可以通过多种方式截获它:

  • 恶意 App 注册相同的自定义 URI scheme:iOS/Android 上,多个 App 可以声明相同的 redirect_uri scheme。用户授权后,OS 不确定该把 redirect 交给哪个 App——恶意 App 抢先接收,拿到 code,换取 access_token
  • 浏览器扩展 / 恶意脚本:如果 RP 是 SPA,redirect 处理页面可能是攻击者可脚本注入的。
  • Referer Header 泄露:如果 redirect 页面加载了第三方资源(图片、脚本),请求的 Referer Header 会携带完整 URL,包括 code 参数。

PKCE 的防御逻辑:在授权请求时 RP 发送 code_challengecode_verifier 的 SHA-256 哈希),在 /token 端点再发送 code_verifier 原文——OP 哈希后比对。攻击者即便截获了 code,没有 code_verifier 也无法换取 access_token

2.2 PKCE 的密码学细节

sequenceDiagram
    participant RP as 应用 (RP)
    participant User as 浏览器
    participant OP as 授权服务器 (OP)

    Note over RP: 1. 生成 code_verifier
    Note over RP: code_verifier = 43-128 chars<br/>随机字符串 [A-Za-z0-9._~-]
    Note over RP: 2. 计算 code_challenge
    Note over RP: code_challenge = BASE64URL(SHA256(code_verifier))

    RP->>User: 3. GET /authorize?code_challenge=XYZ&code_challenge_method=S256
    User->>OP: 4. 浏览器跳转到 OP
    OP->>User: 5. 用户认证并授权
    OP->>User: 6. 302 redirect_uri?code=AUTH_CODE

    RP->>OP: 7. POST /token (code + code_verifier)
    Note over OP: 8. 验证:SHA256(code_verifier) == code_challenge
    OP->>RP: 9. { access_token, ... }

关键参数:

  • code_verifier:43 到 128 字符的高熵随机字符串,字符集 [A-Za-z0-9._~-](unreserved characters)。
  • code_challenge:对 code_verifier 做 SHA-256 哈希后 Base64URL 编码的结果。
  • code_challenge_methodS256(SHA-256)或 plain(不建议——plain 等于没做 PKCE)。

工程陷阱 1code_challenge_method=plain 不提供任何保护——攻击者截获 code 的同时也截获了 code_challengecode_challenge 就是 code_verifier 原文),可以直接用来请求 /token。OAuth 2.1 要求 OP 拒绝 code_challenge_method=plain

2.3 为什么机密客户端也需要 PKCE

后端服务器可以安全存储 client_secret,但 code 仍然经过浏览器。攻击者可以截获 code 后,赶在合法客户端之前用 code 请求 /token(authorization code replay)。PKCE 的 code_verifier 只在 RP 后端持有,不经过浏览器——攻击者没有它就无法完成 code 兑换。这为机密客户端额外增加了一层纵深防御。

三、Refresh Token 的安全加固

OAuth 2.0 的 Refresh Token 设计有一个根本问题:Token 是 Bearer Token(持有者令牌),谁持有它就能用它。如果 Refresh Token 被泄露——通过网络嗅探、日志打印、数据库漏扫——攻击者可以持续获取新的 Access Token,直到 Refresh Token 过期或被吊销。

3.1 Refresh Token Rotation(轮换)

OAuth 2.1 强制要求:每次用 Refresh Token 换新 Access Token 时,OP 必须同时签发新的 Refresh Token,并立即使旧 Refresh Token 失效。

原 Refresh Token (R1)
  → POST /token grant_type=refresh_token refresh_token=R1
  → 返回 { access_token: A2, refresh_token: R2 }
  → R1 立即失效,R2 成为唯一有效 Refresh Token

如果攻击者持有 R1 在合法客户端之前使用:
  → 攻击者用 R1 拿到 A_attacker + R3
  → R1 失效,合法客户端的 R1 不再可用
  → 合法客户端发现 R1 失败 → 告警 → 触发安全响应

如果合法客户端先用 R1:
  → 攻击者的 R1 已经失效 → 攻击失败

这个机制不能阻止 Refresh Token 泄露,但能检测泄露:如果合法客户端使用的 Refresh Token 突然失效,说明有人已经用了它。

3.2 Sender-Constrained Token(发送者约束令牌)

Token 轮换解决了”检测泄露”的问题,但没解决”防止泄露后被使用”。Sender-Constrained Token 把令牌绑定到特定的客户端实例,即使令牌被窃取,攻击者也无法使用。

两种主要机制:

  1. mTLS(RFC 8705):令牌绑定到客户端的 TLS 证书。/token 请求时,OP 验证客户端证书并将 Token 绑定到该证书。后续使用 Token 访问资源时,客户端必须出示相同的 TLS 证书。要求部署 PKI 基础设施。

  2. DPoP(Demonstration of Proof-of-Possession,RFC 9449):应用层方案。客户端生成非对称密钥对,在每次请求时用私钥签名一个 DPoP Proof(JWT 格式),证明”持有 Token 的人和生成 DPoP 密钥对的人是同一个”。不需要 PKI,比 mTLS 更轻量。

DPoP 的核心机制:

客户端生成 key pair(一次,跨会话保持)
  ↓
POST /token 附带 DPoP proof:
  { typ: "dpop+jwt", alg: "ES256", jwk: { 公钥 } }
  { jti: "unique-nonce", htm: "POST", htu: "https://op.example.com/token", iat: ... }

  ↓ OP 验证 DPoP proof → 签发 access_token,绑定到 jwk thumbprint

后续 API 调用:
  Authorization: DPoP eyJhbG...access_token
  DPoP: eyJhbG...(新的 DPoP proof, 相同 jwk, htm/htu 匹配当前请求)

DPoP 是一个相对新的 RFC(2024 年发布),主流 OP 的支持情况不一致。截至 2026 年初,Auth0 和 Microsoft Entra ID 已支持 DPoP,Keycloak 在 24.x 版本中实验性支持。选择 DPoP 时需要评估 OP 的支持情况和客户端的实现成本。

四、精确 Redirect URI 比对与 state 参数

OAuth 2.0 允许 OP 使用”前缀匹配”(prefix matching)来验证 redirect_uri——如果注册的是 https://app.example.com/callbackhttps://app.example.com/callback/attacker 也可能被接受。这导致攻击者可以构造一个指向恶意路径的回调 URL。

OAuth 2.1 要求精确匹配(exact string matching),除非注册的 URI 本身就是一个允许子路径的模式(如 https://app.example.com/callback/*)。

state 参数的作用是绑定授权请求和回调响应——RP 生成随机 state,OP 原样返回,RP 验证一致性。这防止了 CSRF 攻击(攻击者诱导用户点击一个构造的 OAuth 回调 URL,让用户的账户绑定到攻击者的第三方账户)。

工程陷阱 2state 不是 noncestate 绑定授权请求和回调,nonce 绑定授权请求和 ID Token。两者都必须在每次授权请求中生成新值。

五、RAR(Rich Authorization Requests):超越 scope

OAuth 2.0 的 scope 机制过于粗糙——“允许这个 scope 就能做这一类操作”。实际业务需要在授权时表达精细的约束:“只允许对这 3 个仓库的读权限”“只允许转账不超过 1000 元”“只在工作时间允许访问”。

RAR(RFC 9396)引入 authorization_details 参数,用 JSON 结构替代扁平的 scope 字符串:

{
  "authorization_details": [
    {
      "type": "account_information",
      "actions": ["read"],
      "locations": ["https://api.example.com/accounts/123"]
    },
    {
      "type": "payment_initiation",
      "actions": ["create"],
      "locations": ["https://api.example.com/payments"],
      "max_amount": { "value": 1000, "currency": "CNY" }
    }
  ]
}

RAR 的典型应用场景:

  • 金融 API(FAPI 规范):用户可以授权”只允许从这个账户转出不超过 X 元”,而不是给一个”payment” scope。
  • GitHub 式精细授权:授权时指定”只允许访问 repo A 和 repo B”,而不是”所有 repos”。

RAR 在实际落地中受限于 OP 和资源服务器的支持。截至 2026 年,Auth0 和 Okta 支持 authorization_details,Keycloak 在 24.x 版本中有限支持。

六、小结:OAuth 2.1 的”底线配置”

如果你要新部署一个 OAuth 授权系统,以下配置应该作为默认起点(除非有明确理由偏离):

  1. 只使用 Authorization Code Grant + PKCEcode_challenge_method=S256
  2. 所有客户端都使用 PKCE,包括机密客户端。
  3. Refresh Token Rotation(每次换新 token 时发新的 refresh token,旧的立即失效)。
  4. Sender-Constrained Token(有条件的团队使用 DPoP,有 PKI 基础设施的用 mTLS)。
  5. 精确 redirect_uri 匹配
  6. 对所有客户端验证 state 参数
  7. SPA 使用 BFF(Backend for Frontend)架构,不在浏览器端直接处理 token。

上一篇企业单点登录:OIDC 与现代 SSO 下一篇SAML 还值得学吗:企业遗留 SSO 的现实世界

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-13 · architecture / security

【身份与访问控制工程】企业单点登录:OIDC 与现代 SSO

OIDC 是当下企业 SSO 的事实标准,但大多数实现只用了它 20% 的规范。本文从 OIDC 核心规范出发,拆解 Authorization Code Flow + PKCE 的完整交互、ID Token 的验证规则、Discovery 与 Dynamic Registration 的互操作性机制,以及 RP-Initiated Logout 和 Session Management 的工程实现细节。

2026-06-13 · architecture / security

【身份与访问控制工程】IAM 全景:为什么这是高价值赛道

从 2020 年 SolarWinds 到 2024 年 Okta 支持系统泄露,身份基础设施的安全失败反复证明一件事:IAM 不是 IT 支撑系统,而是安全架构的承重墙。本文建立现代 IAM 的全景地图——从认证协议、令牌体系、权限模型到身份治理与平台选型,给出 5 个贯穿全系列的核心问题。

2026-06-15 · architecture / security

【身份与访问控制工程】Session、Refresh Token 与吊销体系

JWT 的无状态设计带来了可扩展性,但让令牌吊销变成了系统性问题——签出去的 JWT 在到期之前全是活令牌。Refresh Token Rotation、Token Introspection、基于事件的吊销通知、撤销列表——这些机制构成了身份系统的'紧急刹车',各自的成本、延迟和覆盖范围完全不同。本文拆解四种吊销机制的工程权衡。

2026-06-17 · architecture / security

【身份与访问控制工程】服务身份:mTLS、SPIFFE/SPIRE 与 Workload Identity

前 9 篇讨论的都是'人'的身份——用户怎么登录、怎么验证。但微服务世界中,80% 的 API 调用是服务之间的。服务身份(Workload Identity)是整个 IAM 体系的另一半:mTLS 解决'传输层你是谁',SPIFFE/SPIRE 解决'在平台层你是谁且怎么证明',JWT Profile for OAuth 解决'我怎么拿到一个服务身份的 Token'。本文从这三条线拆解服务身份的工程实现。