第 69 章 OAuth 2.0 / OIDC 攻击链
OAuth 是"第三方登录"的标准(用微信/GitHub/Google 登录),OIDC 是其身份层扩展。它跨越"客户端、授权服务器、资源服务器"三方,攻击面在信任边界的传递:redirect_uri、state、PKCE、scope、token 处理。本章覆盖完整攻击链与测试方法。
📍 知识点地图 | 主题:OAuth/OIDC | 前置:第58章 | 后续:— | 核心概念:redirect_uri、state、PKCE、token泄漏
69.1 OAuth 2.0 核心流程(先懂四角色)
角色:
资源所有者(用户)
客户端(应用,如你的 App)
授权服务器(AS,如微信/GitHub)
资源服务器(RS,持有用户数据)
Authorization Code Grant(授权码模式,最主流):
1. 用户访问客户端 → 重定向到 AS 授权页
https://as.com/authorize?client_id=X&redirect_uri=https://app.com/callback&state=S
2. 用户授权 → AS 重定向回客户端(带授权码)
https://app.com/callback?code=AUTH_CODE&state=S
3. 客户端用 code+client_secret 换 token(后端换,code 不可见)
4. 客户端用 token 访问 RS
安全关键点(攻击面都在这些环节):
redirect_uri(回调地址可控?)
state(防 CSRF 的随机值)
PKCE(授权码拦截防护)
client_secret(客户端凭证泄露)
scope(权限范围)
token 处理(泄漏/重放)
69.2 攻击面总览
┌──────────────────────────────────────────────────┐
│ 1. redirect_uri 操控(最经典) │
│ → 授权码/令牌被送到攻击者控制的地址 │
│ 2. state 缺失/可预测(CSRF 绑定攻击) │
│ → 攻击者的 code 绑定受害者的会话 │
│ 3. PKCE 缺失/可绕过(授权码拦截) │
│ 4. client_secret 泄露(前端/移动端硬编码) │
│ 5. scope 过度/提升(权限放大) │
│ 6. token 泄漏(Referer/日志/URL) │
│ 7. 重放与跨租户(token 复用) │
│ 8. 开放重定向联动(授权服务器信任的重定向) │
└──────────────────────────────────────────────────┘
69.3 redirect_uri 操控(核心攻击)
变体 1:开放重定向 + redirect_uri
正常: redirect_uri=https://app.com/callback
攻击:
redirect_uri=https://app.com/callback@evil.com # 解析差异
redirect_uri=https://app.com/callback?redirect=https://evil.com # 参数注入
redirect_uri=https://evil.com/?redirect=https://app.com/callback # 开放重定向
流程: 用户授权 → AS 把 code 发给 evil.com → 攻击者拿到 code
变体 2:严格校验的绕过
□ 加前缀/后缀(redirect_uri=https://app.com.evil.com)
□ 路径穿越(redirect_uri=https://app.com/callback/../../evil)
□ 端口变化(redirect_uri=https://app.com:8443/callback —— 有的校验忽略端口)
□ 大小写/编码(%2F、http://)
□ 子域白名单放宽(*.app.com → 攻击者注册 app.com.evil.com 或 test.app.com)
变体 3:二次跳转
redirect_uri=https://app.com/callback(白名单内)
但 callback 端点本身有开放重定向:
https://app.com/callback?next=https://evil.com
→ 授权码最终到达 evil.com
69.4 state 缺失(CSRF 绑定攻击)
原理
state = 随机值,AS 原样返回,客户端校验"请求是自己的"
缺失/固定/可预测 → 攻击者能"用自己的授权"绑定受害者的会话
攻击流程(Login CSRF):
1. 攻击者完成一次授权(拿到自己的 code)
2. 诱导受害者点击: https://app.com/callback?code=ATTACKER_CODE
3. 受害者浏览器登录了自己的账号(但 code 是攻击者的)
4. 效果: 受害者账号被绑定到攻击者的身份(账号混淆/接管)
测试
□ 登录流程里 state 是否存在
□ state 是否随机(多次登录对比)
□ 删掉 state 还能完成吗
□ 不校验 state(任意值都接受)
69.5 PKCE 缺失/绕过(授权码拦截)
原理
PKCE = 授权码模式的安全增强(防"授权码被拦截后兑换")
客户端先发 code_challenge(随机值的哈希)→ 兑换时带 code_verifier
拦截者没有 verifier → 换不了 token
缺失 PKCE 的危害(配合劫持回调):
攻击者拿到 code(通过开放重定向/中间人)→ 直接换 token
测试
□ 授权请求无 code_challenge 参数 → 无 PKCE
□ 有 PKCE 但服务端不校验 verifier
□ code_challenge 可预测(固定值)
69.6 client_secret 与 scope 问题
secret 泄露(移动端/前端硬编码)
□ 移动 App 逆向 → 找 client_id/client_secret(第 10 章)
□ 前端 JS → 找嵌入的 secret(第 13 章)
□ 泄露后果: 冒充客户端 → 配合 redirect_uri 攻击
scope 提升(权限放大)
测试: 请求更高的 scope
scope=read → scope=read%20write
scope=user:info → scope=user:admin
服务端可能: 不校验 scope 合法性/不校验客户端申请范围
OIDC 变体: 请求额外的 claims(profile/email/phone/address)
69.7 token 泄漏与重放
泄漏渠道
□ Referer 头: 回调页面加载外部资源 → Referer 带 token
□ URL fragment: implicit 模式(token 在 # 后)→ 浏览器历史/扩展
□ 日志: 服务端/代理记录完整 URL
□ 第三方脚本: 页面加载的统计/分析脚本可能看到
重放与跨租户
□ token 重放: 同一 token 多次使用(应只有效一次/短时效)
□ 跨租户: A 租户的 token 访问 B 租户资源(多租户 SaaS)
□ refresh token 滥用: 无限续期/永不过期
□ token 不绑定客户端/设备(可跨设备使用)
69.8 测试工具与流程
# 工具
# 1. Burp + OAuth Scanner 扩展(自动测试常见 OAuth 漏洞)
# 2. 手工: 拦截授权流程,逐环节篡改
# 3. jwt_tool(OAuth 拿到的 token 再测 JWT 层,第 58.7)
# 4. Postman: 复现完整 OAuth 流程做参数篡改
# 完整测试流程
1. 梳理 OAuth 集成(找所有第三方登录入口)
2. 记录完整授权流程(authorize → callback → token → API)
3. 逐环节测试:
□ redirect_uri(五类绕过)
□ state(缺失/可预测/不校验)
□ PKCE(缺失/不校验)
□ scope(提升)
□ token(泄漏/重放/跨租户)
4. 联动测试: 开放重定向/前端逆向找 secret
5. 报告: 攻击链 + 每步证据
69.9 检查清单
□ OAuth 集成点枚举(第三方登录入口)
□ redirect_uri 操控(5 类绕过变体)
□ state 缺失/可预测
□ PKCE 缺失/不校验
□ client_secret 泄露(前端/移动端)
□ scope 提升
□ token 泄漏(Referer/日志/URL)
□ token 重放/跨租户/刷新滥用
□ 开放重定向联动
□ 报告: 完整攻击链
动手练习
- 在练习靶场(如 PortSwigger OAuth labs)完成 redirect_uri 绕过实验。
- 构造一个"state 缺失"的 Login CSRF PoC,验证账号绑定攻击。
- 用前端逆向找一个练习应用硬编码的 client_secret(第 13 章方法)。
- 画一张 OAuth 攻击链图:从 redirect_uri 到 token 被窃取的完整路径。
深入阅读
- 仓库:
skills/api-security/references/jwt-oauth-testing.md(OAuth 测试全量) - 仓库:
CTF-Sandbox-Orchestrator/competition-oauth-oidc-chain/(OAuth CTF 子技能) - 仓库:
CTF-Sandbox-Orchestrator/competition-jwt-claim-confusion/(JWT 声明混淆) - 仓库:
skills/api-security/SKILL.md(API 安全方法论) - 仓库:
skills/js-reverse/(前端找 secret/token)