OAuth2 威胁模型与攻击面¶
本文你会学到:
- 🎯 RFC 6819 威胁模型的攻击假设和架构假设
- 🎯 OAuth2 各角色面临的威胁及攻击面
- 🎯 授权码流程中的关键攻击场景和防御策略
- 🔑 令牌安全的核心设计原则(Scope 限制、一次性使用、受众绑定、签名加密)
- 🏛️ 授权服务器端的安全职责
各安全机制的实现细节(PKCE、state、Refresh Token Rotation、DPoP 等)详见「安全实践」。
🤔 什么是威胁模型¶
威胁模型就是系统地回答三个问题:谁可能攻击我们?他们能攻击哪里?我们怎么防?
RFC 6819(OAuth 2.0 Threat Model and Security Considerations)是 OAuth2 生态中最重要的安全文档之一,它全面梳理了 OAuth2 协议中的威胁和对应的防护策略。本文基于该文档提炼关键内容。
⚠️ 攻击假设¶
在开始分析之前,RFC 6819 先做了三个**最坏情况假设**——安全设计必须在这些假设成立时仍然有效:
- 攻击者拥有完整的网络访问权限——可以窃听客户端与授权服务器、客户端与资源服务器之间的所有通信
- 攻击者资源无限——不限制攻击者的计算能力和时间
- 三方中可能有两方串通——例如客户端和授权服务器都在攻击者的控制下,联手欺骗用户
为什么假设这么极端?
安全设计遵循「零信任」原则:不假设任何环节是安全的。即使实际部署中某些假设过于悲观,按最坏情况设计也能确保系统在大多数场景下是安全的。
🏗️ 架构假设¶
RFC 6819 假设以下数据元素存在于各角色中:
| 角色 | 存储或可访问的敏感数据 |
|---|---|
| 授权服务器 | 用户名和密码、客户端 ID 和密钥、Refresh Token、Access Token(handle 模式)、HTTPS 证书/密钥 |
| 资源服务器 | 用户数据、HTTPS 证书/密钥、授权服务器凭证或共享密钥、Access Token(每次请求) |
| 客户端 | 客户端 ID(和密钥)、Refresh Token(持久化)、Access Token(临时)、受信 CA 证书 |
注意:资源服务器
不知道Refresh Token、用户密码和客户端密钥——这是设计上的安全隔离。
🎯 按角色分组的攻击面¶
客户端面临的威胁¶
获取客户端密钥¶
- 从源码或二进制文件提取:即使代码混淆,密钥也可以被逆向工程提取
- 从 Web 服务器获取:通过攻破安全控制(配置文件、数据库)
- 从原生应用获取:读取设备本地文件系统中的存储
防护策略:
不向公开客户端颁发密钥——这是最根本的防护- Web 服务器使用标准安全措施;原生应用使用系统安全存储(Keychain、Keystore)
- 使用部署特定的客户端密钥(每个部署实例不同),并定期轮换
获取 Refresh Token¶
- Web 应用被攻破影响该站点所有用户,原生应用本地存储泄露通常只影响单个用户
- 设备被盗或克隆导致攻击者获得设备上的所有数据
防护策略:
- 授权服务器验证 Refresh Token 关联的
client_id - 使用 Refresh Token Rotation(详见「安全实践 · Refresh Token Rotation」)
- 原生应用使用安全存储 + 设备锁,支持用户主动撤销
获取 Access Token¶
攻击场景:
- Access Token 存储在可被其他应用访问的存储设备中
- 攻击者通过窃取的 Bearer Token 访问所有关联资源
防护策略:
- Access Token
仅保存在临时内存中,不要持久化存储 - 使用短有效期 Access Token 和发送者约束令牌(详见「安全实践」)
- 限制 Token 的 Scope
嵌入式浏览器钓鱼¶
恶意应用使用嵌入式浏览器(WebView)进行授权,绕过系统浏览器的 TLS 确认等安全机制,截获用户输入的认证信息。
防护策略:
- 禁止使用嵌入式 WebView——使用系统浏览器或平台 API(如 ASWebAuthenticationSession)
- OAuth2 的设计让客户端应用
不需要知道用户密码,客户端开发者不应收集认证信息
授权端点面临的威胁¶
伪造授权服务器钓鱼¶
攻击者通过 DNS 或 ARP 欺骗拦截请求,返回伪造的授权页面诱骗用户输入密码。防护: 授权服务器使用 TLS 保护所有端点,教育用户验证域名和 TLS 证书。
用户授予过多权限¶
用户不理解 Scope 的含义,或被恶意客户端诱导授予了过多权限。防护: 授权确认页用通俗易懂的语言解释权限含义;根据客户端类型调整 Scope;允许用户只批准部分权限。
恶意客户端冒充合法客户端¶
攻击场景:
恶意客户端注册与合法客户端相同的 redirect_uri,利用合法客户端已获得的用户自动授权来获取资源。
防护策略:
不要对无法可靠验证的客户端自动处理重复授权- 验证预注册的
redirect_uri - 限制自动授权的 Scope 范围
- 在自动授权时仍要求用户输入(如 CAPTCHA)以防止屏幕抓取
授权码注入攻击¶
攻击场景:
这是一个精巧的攻击——攻击者修改合法应用的授权链接,将 redirect_uri 指向自己的服务器,然后诱使受害者点击。受害者完成授权后,授权码被发送到攻击者的服务器,攻击者再用这个授权码在合法客户端上注入受害者的资源。
sequenceDiagram
participant 攻击者
participant 受害者
participant AS as 授权服务器
participant 合法客户端
攻击者->>受害者: 1. 诱骗点击被篡改的授权链接<br/>(redirect_uri 指向攻击者服务器)
受害者->>AS: 2. 完成授权
AS-->>攻击者: 3. 授权码发送到攻击者的服务器
攻击者->>合法客户端: 4. 构造含授权码的合法 redirect_uri<br/>注入到攻击者自己的账户
合法客户端->>AS: 5. 用授权码换 Token
AS-->>合法客户端: 6. 返回 Access Token
Note over 攻击者: 7. 用攻击者账户访问受害者的资源 ✅
防护策略:
- 授权服务器
将授权码绑定到 redirect_uri——令牌请求中的redirect_uri必须与授权时的完全匹配 - 强制使用预注册的
redirect_uri - 使用部署特定的客户端密钥
- 使用 PKCE(详见「授权流程 · PKCE」和「安全实践 · PKCE」)
令牌端点面临的威胁¶
窃听 Access Token¶
攻击者在通信中窃听 Access Token。所有通信必须使用 TLS 保护;如果端到端机密性无法保证,应缩短 Token 有效期并限制 Scope。
数据库泄露¶
攻击者通过获取数据库访问权限或 SQL 注入获取 Access Token、授权码或客户端密钥。
防护策略:
- 系统安全措施(最小权限、网络隔离)、SQL 注入防护(参数化查询)
只存储凭证的哈希值,不存储明文
在线猜测攻击¶
攻击者尝试暴力猜测 client_id/client_secret 组合或授权码值。防护措施见「安全实践 · RFC 6749 安全考量补充」。
🔑 令牌安全设计原则¶
RFC 6819 在 Section 5.1.5 中给出了令牌安全的几项核心原则:
限制 Scope¶
授权服务器可以基于多种策略限制 Token 的 Scope:
- 基于客户端类型(公开客户端获得更小的 Scope)
- 基于服务的敏感程度
- 基于用户的个人设置
这降低了 Token 泄露后的影响——即使 Token 被截获,攻击者也只能访问有限的资源。
确定合理的有效期¶
Token 有效期越短,泄露后的影响窗口越小。有效期由以下因素决定:
| 因素 | 影响 |
|---|---|
| Token 泄露的风险 | 风险越高,有效期越短 |
| 底层授权的持续时间 | 长期授权可配较长有效期 |
| 授权修改的生效时效 | 需要快速生效时用短有效期 |
| 攻击者猜测/伪造 Token 的时间 | 有效期应短于猜测成功的时间 |
例如:支付交易的 Token 可能只有几分钟有效期,而读取联系人的 Token 可能持续几小时。短有效期的具体建议见「安全实践 · RFC 6750 安全建议」。
限制使用次数或一次性使用¶
授权码应该是一次性的。如果授权服务器观察到多次尝试兑换同一个授权码,应撤销基于该授权码颁发的所有 Token。
Access Token 也可以限制使用次数,迫使客户端重新获取新的 Token。
将 Token 绑定到特定受众和客户端¶
- 绑定受众(
aud):在多服务环境中,授权服务器应将 Token 绑定到目标资源服务器,防止 Token 被重用到其他资源服务器或被恶意资源服务器滥用 - 绑定
client_id:授权服务器可以将 Token 绑定到特定客户端标识,每次使用 Token 时验证 client_id 是否匹配,以检测泄露和滥用
签名和加密¶
| 措施 | 目的 |
|---|---|
| 签名自包含 Token | 防止篡改和伪造(JWT 的 JWS) |
| 加密 Token 内容 | 保护敏感声明不被泄露(JWT 的 JWE) |
| 使用标准断言格式 | 推荐使用 JWT 等标准格式,不自己造轮子 |
Access Token 存储和使用建议¶
仅保存在临时内存中,不要持久化;使用 TLS 传输- 客户端应用不应将 Token 共享给第三方
🛡️ 凭证保护最佳实践¶
| 措施 | 说明 |
|---|---|
| 不明文存储 | 存储哈希值而非明文;密码类凭证额外加盐(salt) |
| 加密存储 | 客户端凭证使用密钥库(keystore)或加密数据库存储 |
| 不嵌入代码 | 不要将客户端密钥硬编码在源代码或二进制文件中 |
| 系统安全 | 服务器端最小权限、网络隔离、文件权限控制 |
在线攻击防御(高熵密钥、账户锁定、时间陷阱、CAPTCHA 等)详见「安全实践 · RFC 6749 安全考量补充」。
🏛️ 授权服务器的安全职责¶
授权码安全¶
如果授权服务器检测到**同一个授权码被多次尝试兑换**,应撤销基于该授权码颁发的所有 Token。这是授权码注入攻击的关键防御手段。
Refresh Token 安全¶
- 限制签发:对于不信任的客户端,可以拒绝签发 Refresh Token
- 绑定 client_id:每次刷新请求验证客户端身份
- Rotation:每次刷新后颁发新的 Refresh Token,旧 Token 立即失效(详见「安全实践 · Refresh Token Rotation」)
- 撤销机制:支持客户端或用户主动撤销 Refresh Token
客户端认证和授权¶
- 不向安全策略不当的客户端颁发密钥;公开客户端必须获得用户明确同意
- 验证预注册的
redirect_uri(精确匹配),不要对无法验证的客户端自动处理重复授权 - 使用更强的客户端认证方式(如 Private Key JWT)
用户授权¶
- 用通俗易懂的语言展示授权范围,允许用户只批准部分权限
- 将授权码绑定到
client_id和redirect_uri,允许用户验证客户端身份信息
小结:RFC 6819 的核心思想是「纵深防御」——没有单一的安全措施能应对所有威胁,需要多层防护互相配合:TLS 保护传输、PKCE 保护授权码、state 防 CSRF、短有效期限制泄露影响、Scope 限制权限范围、Refresh Token Rotation 检测被盗。每一层都减少攻击者的机会,叠加起来构成坚固的安全体系。