题目
设计防御OAuth 2.0授权码注入攻击的安全方案
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
OAuth 2.0协议安全, CSRF防御, PKCE机制, 重定向URI验证, 令牌安全存储
快速回答
防御OAuth 2.0授权码注入的核心方案:
- 强制实施PKCE(Proof Key for Code Exchange)机制
- 严格验证重定向URI的完整性和白名单匹配
- 使用加密的state参数防御CSRF
- 授权码单次有效且短生命周期(≤10分钟)
- 客户端认证与令牌绑定(Token Binding)
原理说明
授权码注入攻击发生在攻击者窃取授权码并注入到自己的客户端中。OAuth 2.0的脆弱点包括:
- 授权码劫持:通过中间人攻击或开放重定向窃取授权码
- CSRF:诱导用户使用攻击者的授权码完成认证流程
- 客户端冒充:未经验证的客户端使用窃取的授权码获取令牌
防御方案与代码示例
1. PKCE 实现(关键防御)
// 客户端生成PKCE参数
const crypto = require('crypto');
// 步骤1:生成code_verifier(43-128字符)
const codeVerifier = crypto.randomBytes(32).toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
// 步骤2:生成code_challenge
const codeChallenge = crypto.createHash('sha256')
.update(codeVerifier)
.digest('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
// 授权请求中添加参数
const authUrl = `https://auth-server/authorize?response_type=code
&client_id=CLIENT_ID
&redirect_uri=https://client/callback
&code_challenge=${codeChallenge}
&code_challenge_method=S256
&state=CRYPTO_RANDOM_STATE`;服务端验证逻辑:
- 存储授权请求中的code_challenge
- 兑换令牌时验证code_verifier的哈希匹配code_challenge
2. 重定向URI安全验证
# 服务端验证示例 (Python/Flask)
def validate_redirect_uri(client_id, redirect_uri):
# 从数据库获取客户端注册的URI白名单
allowed_uris = get_client_redirect_uris(client_id)
# 严格匹配(包括query参数)
if redirect_uri not in allowed_uris:
abort(400, "Invalid redirect URI")
# 额外检查:防止开放重定向
parsed = urlparse(redirect_uri)
if parsed.scheme != "https" or not parsed.hostname.endswith(".trusted-domain.com"):
abort(400, "Untrusted redirect target")3. State参数加密处理
// Java示例:生成加密state
public String generateSecureState(String sessionId) {
String state = UUID.randomUUID().toString();
// 绑定会话ID并加密(使用AES-GCM)
String payload = sessionId + "|" + state;
return encrypt(payload, SECRET_KEY);
}
// 回调时验证
public void validateState(String encryptedState, HttpSession session) {
String payload = decrypt(encryptedState, SECRET_KEY);
String[] parts = payload.split("\\|");
if (!session.getId().equals(parts[0])) {
throw new SecurityException("State session mismatch");
}
// 验证state随机值未被篡改
}最佳实践
- 令牌安全:
- 访问令牌有效期≤1小时,刷新令牌≤90天
- 使用JWT格式令牌时添加`jti`(JWT ID)防止重放
- 客户端分级:
- 公共客户端(SPA/移动端)必须使用PKCE
- 机密客户端(服务端)需定期轮换client_secret
- 监控审计:
- 记录授权码使用IP/设备指纹异常
- 实时警报多次失败的令牌兑换请求
常见错误
- 重定向URI验证不足:
- 错误:仅验证域名不验证完整路径,导致子路径开放重定向
- 修复:严格全匹配注册URI(含协议/端口/路径)
- PKCE实现缺陷:
- 错误:使用`code_challenge_method=plain`(明文传输)
- 修复:强制使用`S256`哈希算法
- 令牌存储泄露:
- 错误:浏览器localStorage存储访问令牌
- 修复:机密数据使用HttpOnly Cookie + 服务端会话存储
扩展知识
- OAuth 2.1规范:
- 强制要求PKCE(RFC 7636)
- 废除隐式授权(implicit grant)
- 重定向URI必须精确匹配
- 进阶防御:
- 令牌绑定(Token Binding):将令牌与TLS会话关联
- Dpop(Demonstrated Proof-of-Possession):客户端密钥证明
- 相关漏洞案例:
- Facebook 2018漏洞:重定向URI解析差异导致令牌泄露
- Azure AD 2021漏洞:PKCE实现缺陷允许授权码注入