3. 授权码请求与颁发:参数规范与凭证生命周期
授权码请求是 OAuth2.0 授权码模式中客户端与授权服务器之间的第一次关键交互。本节将深入解析请求参数的标准定义、授权码的生成策略以及存储合规要求,帮助审计人员建立参数级与凭证级的检查基线。
3.1 授权码请求参数详解
客户端在发起授权请求时,必须按照 RFC 6749 Section 4.1.1 构造请求。以下表格列出了五个核心参数及其审计关注点:
| 参数 | 是否必选 | 合法值/格式 | 审计检查要点 |
|---|---|---|---|
response_type |
必选 | 固定值 code |
是否被篡改为 token 或 id_token,防止隐式模式混用 |
client_id |
必选 | 注册时颁发的唯一标识符 | 是否为空、是否在授权服务器白名单中、是否与 redirect_uri 绑定 |
redirect_uri |
条件必选 | 已注册的 URI(支持精确匹配或模式匹配) | 是否与注册 URI 完全一致(防止开放重定向攻击) |
scope |
可选 | 空格分隔的权限标识符(如 openid profile email) |
是否超出客户端已授权的 scope 范围;是否包含敏感 scope |
state |
强烈推荐 | 不可预测的随机字符串(至少 128 位熵) | 是否存在;是否在回调时被校验;是否使用固定值或可预测值 |
审计红线: 若授权服务器允许
redirect_uri 参数与注册 URI 存在差异(如仅验证域名),则构成开放重定向漏洞,属于严重不合规项。审计时应重点检查授权服务器的 URI 校验逻辑是否采用“完全匹配”策略。
3.2 授权码请求流程(SVG)
下图展示了客户端通过用户代理(浏览器)向授权服务器发起授权码请求的完整交互过程:
3.3 授权码的生成策略
授权码(Authorization Code)是授权服务器颁发给客户端的临时凭证,其安全性直接影响整个协议的可信度。生成策略应满足以下合规要求:
- 高熵随机性: 必须使用密码学安全的伪随机数生成器(CSPRNG),推荐长度至少 128 位(如 32 字节的随机字节序列经 Base64URL 编码)。
- 不可预测性: 禁止使用自增序列、时间戳、用户 ID、客户端 ID 等可预测信息作为授权码的组成部分。
- 一次性使用: 授权码被成功兑换令牌后必须立即失效,且失效记录应持久化以防止重放攻击。
- 短生命周期: 授权码的有效期通常为 30 秒至 10 分钟,超过 TTL 后自动失效。审计时应检查授权服务器是否强制实施过期策略。
生成示例(Python):
import secrets
import base64
def generate_authorization_code() -> str:
"""生成符合 OAuth2.0 安全要求的授权码"""
random_bytes = secrets.token_bytes(32) # 256 位熵
code = base64.urlsafe_b64encode(random_bytes).rstrip('=').decode('ascii')
return code
# 示例输出: "5xP0mR8qL3vN9wK2yB7cJ4fA1gH6sD0e"
3.4 授权码的存储与生命周期管理
授权码在授权服务器端的存储方式直接影响审计追踪能力。以下是推荐的存储模型与审计字段:
| 字段名 | 类型 | 说明 | 审计用途 |
|---|---|---|---|
code |
VARCHAR(128) | 授权码明文(或哈希值) | 唯一标识一次授权请求 |
client_id |
VARCHAR(64) | 发起请求的客户端标识 | 追溯客户端身份 |
redirect_uri |
VARCHAR(512) | 请求中使用的重定向 URI | 校验回调一致性 |
scope |
VARCHAR(256) | 请求的权限范围 | 验证令牌颁发时的 scope 是否一致 |
state |
VARCHAR(128) | 客户端提供的防伪造状态值 | 审计 CSRF 防护是否生效 |
user_id |
VARCHAR(64) | 授权用户的唯一标识 | 用户级授权审计 |
expires_at |
DATETIME | 授权码过期时间 | 检查是否在有效期内使用 |
used_at |
DATETIME NULL | 授权码被兑换的时间 | 检测重复兑换行为 |
created_at |
DATETIME | 授权码创建时间 | 审计时间窗口 |
3.5 授权码存储与校验流程(SVG)
下图展示了授权码从生成到兑换的完整生命周期,以及审计系统如何介入检查:
3.6 常见审计发现与整改建议
审计发现 #1:授权码使用可预测模式
某系统授权码格式为
整改: 替换为 CSPRNG 生成的随机字符串,并确保授权码与用户会话强绑定。
某系统授权码格式为
CODE_20250321_0001,包含日期和自增序号。攻击者可枚举授权码,提前兑换他人授权码。整改: 替换为 CSPRNG 生成的随机字符串,并确保授权码与用户会话强绑定。
审计发现 #2:授权码未设置过期时间
授权码在数据库中永久有效,攻击者可利用泄露的授权码在任意时间点兑换令牌。
整改: 设置 TTL 不超过 10 分钟,并在兑换时检查
授权码在数据库中永久有效,攻击者可利用泄露的授权码在任意时间点兑换令牌。
整改: 设置 TTL 不超过 10 分钟,并在兑换时检查
expires_at 字段。
审计发现 #3:state 参数缺失或固定值
客户端未传递
整改: 强制要求客户端生成并校验 state 参数,授权服务器应在回调时验证 state 一致性。
客户端未传递
state 参数,或使用固定字符串 "123456",导致授权码回调存在 CSRF 攻击风险。整改: 强制要求客户端生成并校验 state 参数,授权服务器应在回调时验证 state 一致性。
3.7 审计检查清单
在进行授权码请求与颁发环节的合规审计时,建议逐项核对以下清单:
- 参数完整性: 授权请求是否包含
response_type=code和有效的client_id? - 重定向 URI 校验: 授权服务器是否对
redirect_uri执行完全匹配校验? - Scope 边界: 授权服务器是否校验请求的 scope 未超出客户端注册时允许的范围?
- State 强制使用: 授权服务器是否要求客户端必须提供
state参数? - 授权码熵值: 授权码生成是否使用 CSPRNG,且长度不低于 128 位?
- 一次性使用: 授权码被兑换后是否立即标记为已使用,并拒绝重复兑换?
- 过期策略: 授权码是否设置了合理的 TTL(建议 ≤ 10 分钟)?
- 审计日志: 授权码的生成、兑换、失效事件是否被完整记录,且日志不可篡改?