本报告是 2026-08-07 对 fulla(时名 authforge)的完整 OAuth/OIDC 合规尽调 基线快照:逐条附 file:line 证据与规范章节引文。报告发布后所有发现均已在 后续版本中修复(含 F-002 Critical 凭据哈希不一致等),并沉淀为回归测试; 保留本档案作为第三方评估者可核查的信任资产。当前安全设计见 security-architecture.md。
OAuth/OIDC 规范性审查报告 — fulla
| 项目 | 内容 |
|---|---|
| 审查对象 | fulla (test/coverage-push 分支, 2026-08-07) |
| 审查范围 | RFC 6749 / 6750 / 7662 / 7009 / 7636 / 8252 / 8628 / 8414 / 7519/7517/7515 / 7591/7592 / 9068 / 9700 + OIDC Core 1.0 / Discovery 1.0 / RP-Initiated Logout / Back-Channel Logout |
| 审查方法 | 静态代码审查 + 规范条款逐条核验;所有判断附 file:line 证据 + 规范章节引文 |
| 不在范围 | 动态渗透测试、性能/可用性评估、前端 SPA 实现审查 |
| 评级分档 | 核心规范按 MUST/SHOULD 分档;OIDC profile 按「核心 MUST」「扩展 SHOULD」分档 |
配套文档:审查计划(检查要点、检查方法、判定标准)见
oauth-oidc-compliance-audit-plan.md。本报告为评估结果(符合性评级、发现、整改)。
1. 执行摘要
1.1 总体符合度
fulla 在 **OAuth 2.0 核心(RFC 6749)的"快乐路径"**上基本合规:授权码与刷新令牌的生成、哈希存储、单次性、轮换、重用级联吊销都按规范实现;PKCE 的 S256 算法是规范正确的 base64url(raw digest);introspect/revoke 的客户端认证模型近期(commit 246db32)已修正为 RFC 7662/7009 要求的客户端凭证模型。
但存在 若干违反 MUST 的实质性偏差,集中在三类:
- 令牌端点客户端认证不完整 ——
refresh_tokengrant 完全跳过客户端认证(违反 RFC 6749 §3.2.1 MUST)。 - client_secret 哈希写入/校验算 法不一致 —— 注册/管理路径写无盐大写 SHA-256,校验路径算有盐小写 SHA-256(违反 RFC 6749 §10.6 凭证保护 MUST,且事实上导致动态注册的客户端无法认证)。
- OIDC 扩展能力大面积缺失 ——
prompt/max_age/auth_time/acr/amr/azp/RP-Initiated Logout/nonce 防重放/id_token-on-refresh 均未实现,使本项目实际只能算"OAuth2 + 一个最小 id_token 签发",而非完整 OIDC Provider。
1.2 风险等级分布
| 等级 | 数量 | 代表项 |
|---|---|---|
| 严重 (Critical) | 1 | F-002 client_secret 哈希不一致导致动态注册客户端认证失败 |
| 高 (High) | 6 | refresh_token 无客户端认证、Redis 非常量时间比较、Redis refresh 存储空操作、authorization 端点错误未按 §4.1.2.1 重定向、device_authorization 未认证机密客户端、WWW-Authenticate Bearer 缺失 |
| 中 (Medium) | 9 | id_token 缺 auth_time/acr/amr/azp、refresh 不重发 id_token、PKCE 默认不强制、device slow_down 未发出、iss 硬编码、loopback 例外未实现、token 端点无限流、state 未 urlEncode、token_endpoint_auth_method 未持久化 |
| 低 (Low) | 7 | registration_endpoint 未广告、introspection 缺 jti/username、成功响应缺 Cache-Control、jti 不存在、claims_supported 与实际不符、OpenAPI grant_type 枚举缺 device_code、CORS 校验注释 |
1.3 最高优先级 5 项
| 编号 | 问题 | 风险 | 修复复杂度 |
|---|---|---|---|
| F-002 | client_secret 哈希写/读算法不一致 | Critical | 中(统一算法 + 数据迁移) |
| F-003 | refresh_token grant 跳过客户端认证 | High | 低(加 validateClient 调用) |
| F-004 | Redis 后端 client_secret 非常量时间比较 | High | 低 |
| F-005 | Redis 后端 refresh_token 存储为空操作 | High | 中(实现 Redis save/getRefreshToken) |
| F-007 | authorization 端点错误未按 §4.1.2.1 重定向 | High | 中(区分 redirectable vs client 错误) |
2. 评级尺度
| 评级 | 定义 |
|---|---|
| 符合 | 满足规范条款的 MUST 与 SHOULD,无功能/安全偏差 |
| 部分符合 | 实现核心要求但缺字段、缺边角 MUST 或不满足 SHOULD |
| 不符合 | 违反某条 MUST,或实现存在功能性/安全性错误 |
| 未实现 | 规范定义了能力但代码无对应实现(含 stub、advertised-but-missing) |
OIDC profile 分档(按用户要求):
- 核心 MUST 档:OIDC Core 中标 MUST 的条款(如 id_token 必备 claim、UserInfo 须校验 openid scope)
- 扩展 SHOULD 档:OIDC Core 的 SHOULD 条款,以及 Session Management / RP-Initiated Logout / Back-Channel Logout 等独立规范(这些虽非 OIDC Core 的 MUST,但是「可互操作的 OIDC Provider」的事实标配)
风险等级独立于评级:一个"未实现"的扩展 SHOULD 可能只是 Medium,而一个"不符合"的核心 MUST 通常是 High/Critical。
3. 逐规范符合性评估
3.1 RFC 6749 OAuth 2.0 Authorization Framework —— 部分符合
3.1.1 §1.6 / §3.1.1 协议须运行于 HTTPS;redirect_uri 须 https —— 不符合(Medium)
- 检查方法:Grep redirect_uri scheme 校验、loopback 例外。
- 证据:
libs/oauth2/include/fulla/oauth2/model/Client.h:86-90仅做std::find精确字符串匹配;libs/drogon/src/validation/RuleEngine.cc:120-133的 regex^https?://...同时接受 http 与 https;无任何代码强制 https 或实现 RFC 8252 §7.3 / RFC 6749 §3.1.2.1 的 loopback 端口通配。 - 偏差:redirect_uri 注册与匹配接受任意 scheme,未强制 https,未实现 loopback 例外。
- 依据:RFC 6749 §3.1.2.1 "the redirection endpoint SHOULD require the use of TLS";§1.6 明确 TLS 为 MUST 级别的部署前提。
- 见:F-014。
3.1.2 §3.1.2.3 redirect_uri 须精确匹配 —— 符合
- 证据:
Client.h:83-90isRegisteredRedirectUri用std::find(..., redirectUri) != ...end(),注释明示"RFC 6749 §3.1.2.3 requires exact match, not prefix/pattern matching"。authorize 端libs/oauth2/src/protocol/ClientService.cc:32-44调用之;exchange 端PostgresGrantRepository.cc:181-189复核之。 - 判定:精确匹配、无通配、无前缀。符合。