摘要:sub2api v0.1.171及之前版本存在OAuth账户接管漏洞:攻击者控制一个受支持的OAuth身份并知道目标注册邮箱,即可能错误绑定目标账户;v0.1.172已修复核心链路。
一套AI API网关,表面上只是把Claude、OpenAI、Gemini、Grok等上游服务转换成统一接口,实际上同时掌握四类高价值资产:用户身份、上游OAuth凭据与API密钥、预付余额和订阅配额,以及完整的模型调用记录。它不是普通反向代理,而是一块身份、资金、密钥和数据汇聚的控制面。
sub2api在2026年8月披露的OAuth账户接管漏洞,正是这一控制面风险的集中体现。GitHub Issue #5350将受影响范围描述为v0.1.171及之前版本,给出CVSS 3.1基础分8.8。漏洞报告称,攻击者需要控制一个受支持的OAuth身份,并知道目标账户的注册邮箱;满足这两个前提后,就可能把自己的第三方身份错误绑定到目标用户,之后以目标用户身份登录,接触其API密钥、余额和订阅额度。攻击不需要目标用户交互,也不需要掌握目标密码或邮箱验证码。
这不是OAuth协议本身被攻破,而是应用在OAuth回调之后设计了一套复杂的“待完成登录”流程,却把“发现一个既有邮箱”误当成了“证明操作者拥有该邮箱”。一次状态机放行、一次身份绑定调用和一个过度宽松的权限判断叠加,最终把资料补全页面变成了账户所有权转移通道。
先校正修复状态:报告中的“尚无修复版”已经过时
Issue #5350发布时写明“固定版本暂无,PR等待合并”,这是披露当时的现场状态,不能继续当成当前结论。GitHub API显示,修复PR #5345已于8月7日15:20 UTC合并,合并提交为8991574;约5分钟后,项目创建v0.1.172发行版本,并于当日15:38 UTC正式发布(距补丁合并约18分钟)。该版本说明明确列出:“修复OAuth登录补全流程的账号接管漏洞,非终态会话不再执行身份绑定。”
因此,截至本文核验时,准确表述是:漏洞报告将v0.1.171及之前版本列为受影响版本,核心修复已经合并并进入v0.1.172正式发行版。 Issue本身当时仍处于打开状态,不代表补丁未发布。运营者不应只看Issue状态,而应同时核对合并提交、Release说明、容器镜像标签和本地实际运行版本。
升级到v0.1.172是最低动作,不是全部处置。身份绑定一旦写入auth_identities,单纯升级代码不会自动识别和删除历史异常关系;潜在泄露的上游密钥、平台API Key、刷新令牌和会话也不会因二进制更新自动失效。
OAuth identity binding究竟绑定了什么
OAuth登录通常包含两层身份。上游提供方确认“这个人是某个provider subject”,应用再把该外部身份映射到本地用户。sub2api的auth_identities可理解为一张映射表:由provider类型、provider key和provider subject定位本地user_id。一旦映射建立,后续登录不必重新询问“你要进入哪个本地账户”,系统会直接把该OAuth主体解析成已经绑定的用户。
这正是identity binding的敏感性:绑定不是修改头像或昵称,而是在写入一条长期有效的账户所有权关系。正常的绑定至少应满足两种证明之一:操作者已经登录目标账户,并在该登录态下主动绑定;或者操作者完成目标账户原有凭据、邮箱验证码、2FA等足以证明所有权的再次认证。
sub2api为了处理第三方身份缺少邮箱、邮箱已存在、创建新账户、绑定已有账户以及是否采用上游头像昵称等情况,引入了pending session。它暂存OAuth主体、intent、TargetUserID、浏览器会话和流程步骤,等待前端补全决策。这类设计本身合理,危险在于pending session不是完整身份,只是一份尚未完成验证的流程上下文,不能因为里面出现了一个用户ID就获得绑定权。
三个缺陷如何连成一条账户接管链
第一层缺陷是非穷尽状态机导致fail-open。流程中的choose_account_action_required表示系统仍在等待用户选择或证明账户归属,属于非终态。旧逻辑只对email_completion和bind_login_required等少数步骤提前返回,没有把所有允许绑定的终态明确列成白名单。结果是,一个未被特殊拦截的新状态会继续向下执行。安全状态机应当“只有明确允许才通过”,旧实现却变成“只要没有明确拒绝就通过”。
第二层缺陷是信任边界倒置。当流程发现提交邮箱已存在时,会把pending session的TargetUserID指向该邮箱对应用户,并标记已有账户可以处理。TargetUserID本应是验证成功后的绑定目标,这里却由一个未经证明的邮箱查询结果产生。数据库能够确认“这个邮箱属于某用户”,却不能确认“当前操作者就是该用户”。系统把信息查询结果升级成授权凭据,相当于把“知道门牌号”当成“持有房门钥匙”。
第三层缺陷是adoption与binding语义混杂。adopt_display_name和adopt_avatar只表示是否采用第三方头像、昵称,属于资料偏好;旧流程中的applyPendingOAuthAdoption却会继续调用身份绑定。更关键的是,shouldBindPendingOAuthIdentity在intent=login时曾无条件返回真,没有同时要求session达到可签发令牌的终态,也没有要求邮箱所有权已经验证。匿名发起的login intent反而比已登录用户主动发起的bind_current_user更宽松,违背最小权限原则。
三个问题单独看都像边界处理错误:漏拦一个状态、过早设置一个目标ID、复用一个后处理函数。组合起来却改变了安全语义:未验证pending session获得了选择本地用户、写入身份映射并领取用户令牌的能力。这也是身份系统中最危险的缺陷类型——不是密码学失败,而是“谁有权让系统相信什么”被写错。
CVSS 8.8为什么合理
报告给出的向量是AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H。
AV:N表示攻击面位于网络侧,不要求本地访问;AC:L表示流程虽涉及多个状态,但不依赖竞争条件、特殊时序或罕见部署;PR:L表示攻击者需要拥有一个低门槛的OAuth账户或会话,不是完全匿名;UI:N表示受害者无需点击链接、批准授权或配合操作。S:U表示CVSS把影响仍界定在同一sub2api安全域内。
机密性、完整性和可用性均为高,是因为AI网关账户不仅保存个人资料,还可能控制平台生成的API Key、上游OAuth令牌、订阅账号、余额和调用配额。读取密钥属于机密性损失;新增身份、修改账户和消费资金属于完整性损失;耗尽额度、封禁上游账号或修改认证设置会造成可用性损失。
Issue认为LinuxDo、通用OIDC、微信和钉钉等经过pending-session流程的提供方受影响;GitHub和Google可能走带已验证邮箱的另一条路径,需要独立审计。这里应坚持证据边界:前四类是报告明确指出的范围,后两类不能在没有路径审计的情况下武断宣布安全,也不能直接说已受影响。
修复做对了什么,还缺什么
PR #5345在exchange阶段增加了核心门禁:只有canIssueTokenPair为真的终态登录,或由已登录用户发起的bind_current_user流程,才允许继续执行adoption/binding;其他状态只返回前端所需payload,不绑定身份,也不消费session。回归测试专门验证choice状态不会生成身份映射、不会改写目标用户资料、不会签发令牌,并保留session供正确流程继续处理。
这是必要的fail-closed修复,而且已进入v0.1.172。但PR作者也明确指出,进一步加固并未全部包含在该补丁中:发现既有邮箱时,系统最好转入密码或2FA验证,而不是展示暗示“可直接绑定”的选择状态;历史auth_identities也应检查上游邮箱与本地用户是否存在异常不一致。
AI API网关为什么必须按“金融级身份控制面”治理
普通论坛账户被接管,主要损失可能是资料和发帖权;AI API网关账户被接管,攻击者获得的是可立即兑现的数字资产。上游密钥可以在别处调用,预付余额可以快速消耗,共享订阅可能牵连整个账号池,调用日志还可能包含源代码、合同、客户数据和内部提示词。网关的智能调度会进一步放大权限:一个下游API Key背后可能连接多家供应商和多个高额度账户。
这意味着运营者不能只把安全建设理解为“登录加验证码”。身份、密钥、账单和路由必须联动治理。
第一,所有状态机必须fail-closed。为每个intent × step × authentication proof组合定义允许动作,未知状态默认拒绝;身份绑定、令牌签发、余额操作必须使用独立授权函数,不能依赖前端流程顺序。
第二,绑定必须建立在重新认证上。绑定已有账户应先登录该账户,再从安全设置页发起;若必须在OAuth流程内完成,应强制验证目标邮箱、原密码或2FA,不能仅凭邮箱存在性设置可授权的TargetUserID。
第三,pending session应single-consume且短寿命。成功后原子标记已消费,重放返回失败;会话与浏览器、来源风险和预期provider绑定,敏感状态转换写入不可抵赖审计日志。
第四,立即审计历史身份关系。检查同一provider subject异常关联、短时间批量新增绑定、上游邮箱与本地邮箱不一致、单IP跨多个user_id绑定等信号。发现异常时应先冻结绑定,再通知用户确认,而不是静默删除证据。
第五,按泄露事件轮换凭据。受影响实例升级后,应撤销可疑会话和刷新令牌,轮换平台JWT密钥、管理员凭据、用户平台API Key及可能暴露的上游令牌;高价值账户应分批验证,避免一次性轮换造成业务中断。
第六,密钥存储和运行权限最小化。上游凭据应使用独立KMS或信封加密,应用数据库不应同时持有密文和可直接解密的长期主密钥;后台展示默认脱敏,导出、解密和查看完整密钥需要二次认证与审批。路由进程只获得完成当前请求所需的短期权限。
第七,把账单风控当作安全传感器。账户接管后最早出现的信号往往不是登录失败,而是模型、地域、并发、Token速率和消费曲线突变。应设置余额消耗速度、跨地域调用、新设备绑定、密钥批量创建和共享组配额异常的自动限流与止损规则。
第八,治理开源与镜像供应链。生产环境固定版本和镜像摘要,核验Release校验和,建立SBOM、依赖扫描、升级窗口和回滚方案;不要长期使用不透明的latest标签。安全公告应同时覆盖源码、预编译包、Docker镜像和实际运行资产清单。
最后还要面对合规问题。sub2api README明确提示,使用项目分发AI产品订阅配额可能违反Anthropic等上游服务商条款,项目不提供商业授权,并要求使用者自行承担封号、服务中断和法律风险。技术上能够中转,不等于合同上允许转售;能够保存调用日志,也不等于有权处理客户源代码和个人信息。商业运营者还需评估数据跨境、支付、发票、消费者权益、日志留存和上游账号共享规则。
sub2api事件最值得记住的,不是“邮箱也能接管账户”这一猎奇结论,而是一个更普遍的工程教训:**身份系统的安全,不取决于页面上出现了多少验证码,而取决于每次状态转换是否携带足够的所有权证明。**当AI网关已经成为密钥、资金、模型和数据的统一入口,任何pending、adoption、binding之类看似辅助的流程,都必须按照核心资产控制面来设计和审计。
参考资料
- GitHub Issue #5350:OAuth Account Takeover via Pending Exchange Bypass in sub2api
https://github.com/Wei-Shaw/sub2api/issues/5350 - 修复PR #5345:block OAuth account takeover via pending exchange
https://github.com/Wei-Shaw/sub2api/pull/5345 - 修复合并提交
8991574
https://github.com/Wei-Shaw/sub2api/commit/89915748736c84dbeb2a4ef201a4e2f444f13cab - Sub2API v0.1.172 Release
https://github.com/Wei-Shaw/sub2api/releases/tag/v0.1.172 - Sub2API项目README
https://github.com/Wei-Shaw/sub2api/blob/main/README_CN.md - OAuth pending-session流程源代码
https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/handler/auth_oauth_pending_flow.go - FIRST:CVSS v3.1 Specification
https://www.first.org/cvss/v3.1/specification-document