OAuth PKCE
本文约需 2 分钟阅读
PKCE (Proof Key for Code Exchange,发音为“pixy”) 是为 OAuth 2.0 的授权码流程添加的安全扩展。它于 2015 年作为 RFC 7636 标准化。最初是为移动应用等无法安全保存 client_secret 的公共客户端而设计的,但由于其有效性,OAuth 2.1 正朝着对所有客户端类型强制使用的方向发展。
什么是授权码拦截攻击
要理解 PKCE 所解决的威胁,首先需要了解攻击的原理。在 OAuth 2.0 的授权码流程中,用户在授权服务器登录后,授权码会通过重定向 URI 返回给客户端。在移动操作系统中,重定向使用自定义 URL 方案 (例如 myapp://),但如果恶意应用注册了相同的 URL 方案,就能够拦截授权码。
SPA (单页应用) 也存在类似的风险,因为授权码可能通过浏览器历史记录、引用来源标头或浏览器扩展程序泄露。
code_verifier 与 code_challenge 的原理
PKCE 以密码学方式保证“只有发送授权请求的本人才能获取令牌”。其原理很简单,使用两个值。
| 要素 | 生成时机 | 作用 |
|---|---|---|
| code_verifier | 在授权请求前由客户端生成 | 43-128 个字符的随机字符串。仅由客户端持有的秘密 |
| code_challenge | 由 code_verifier 计算得出 | SHA-256 哈希的 Base64URL 编码。包含在授权请求中 |
即使攻击者拦截了授权码,没有 code_verifier 也无法获取令牌。由于无法从 code_challenge 反推出 code_verifier (SHA-256 的单向性),因此即使截获授权请求也毫无意义。
在公共客户端中的必要性
在传统的 OAuth 2.0 中,机密客户端 (服务器端应用) 可以用 client_secret 保护令牌端点。然而 SPA 和移动应用的源代码掌握在用户手中,因此无法安全地嵌入 client_secret。PKCE 解决了这个问题,即使没有 client_secret 也能证明授权码的合法持有者。
PKCE 作为 CSRF 的防护措施同样有效。与 state 参数不同,PKCE 执行密码学验证,因此提供更稳固的保护。与会话令牌的管理相结合,可以提升整个认证流程的安全性。
在 OAuth 2.1 中的强制化
在 OAuth 2.1 (截至 2025 年仍处于草案阶段) 中,PKCE 对所有客户端类型都成为强制要求。即使是机密客户端也必须使用 PKCE。这基于“纵深防御 (多层防御)”的理念。即使 client_secret 泄露,只要有 PKCE 就能防止授权码被拦截。在使用 OpenID Connect 时,也强烈建议同时使用 PKCE。
常见的实现错误
- 在 code_challenge_method 中使用 plain。由于 plain 会将 code_verifier 原样作为 code_challenge 发送,一旦被截获就毫无意义。务必使用 S256 (SHA-256)
- 将 code_verifier 保存在本地存储 (local storage) 而非会话存储 (session storage) 中。这会增加被 XSS 攻击读取的风险
- code_verifier 的熵不足。应使用密码学安全的随机数生成器 (Web Crypto API 的 getRandomValues)
一并阅读OAuth 权限的陷阱和API 密钥管理的文章,就能看清整个授权流程的安全设计。在实务中,何时改用 API 密钥也是一个重要的判断要点。OAuth 安全相关书籍 (Amazon)也推荐用来进行系统性学习。
这篇文章对您有帮助吗?