跳转至

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 先做了三个**最坏情况假设**——安全设计必须在这些假设成立时仍然有效:

  1. 攻击者拥有完整的网络访问权限——可以窃听客户端与授权服务器、客户端与资源服务器之间的所有通信
  2. 攻击者资源无限——不限制攻击者的计算能力和时间
  3. 三方中可能有两方串通——例如客户端和授权服务器都在攻击者的控制下,联手欺骗用户

为什么假设这么极端?

安全设计遵循「零信任」原则:不假设任何环节是安全的。即使实际部署中某些假设过于悲观,按最坏情况设计也能确保系统在大多数场景下是安全的。

🏗️ 架构假设

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_idredirect_uri,允许用户验证客户端身份信息

小结:RFC 6819 的核心思想是「纵深防御」——没有单一的安全措施能应对所有威胁,需要多层防护互相配合:TLS 保护传输、PKCE 保护授权码、state 防 CSRF、短有效期限制泄露影响、Scope 限制权限范围、Refresh Token Rotation 检测被盗。每一层都减少攻击者的机会,叠加起来构成坚固的安全体系。


上一篇: 安全实践 下一篇: 实战:授权服务器