# 安全漏洞:Subapi API Key 跨部署验证问题 > 发现时间:2026-03-18 > 漏洞等级:**严重(P0)** > 状态:历史漏洞分析文档(用于复盘),不作为当前实现基线。 > 实施基线:`security_solution_v1_2026-03-18.md`(HMAC-SHA256 方案)。 --- ## 1. 漏洞描述 ### 1.1 问题现象 Subapi 分发给用户的 API Key 和激活码只验证算法正确性,未验证 Key 是否由当前系统生成。 这意味着: - 部署在 A 服务器的 Subapi 生成的 API Key,可以在部署在 B 服务器的 Subapi 中通过验证 - 不同独立部署之间的 API Key 可以互相串用 ### 1.2 漏洞原理 ```python # Subapi 当前的验证逻辑(推测) def verify_api_key(key): # 只验证格式和算法 if validate_format(key) and validate_checksum(key): return True # 通过验证 return False # 问题:没有验证 Key 的来源(哪个部署生成的) ``` ### 1.3 影响范围 | 场景 | 影响 | |------|------| | 平台间串用 | 用户的 Key 可能在其他平台也能用 | | 账号盗用 | 窃取的 Key 可以在任意部署使用 | | 收益损失 | 供应方的配额可能被其他平台盗用 | | 账务错误 | 调用记录和计费可能记到错误平台 | --- ## 2. 漏洞影响我们的规划 ### 2.1 如果集成 Subapi - 我们的用户可能使用其他 Subapi 部署生成的 Key - 我们的计费可能被绕过 - 供应方的收益可能被截取 ### 2.2 解决方案 **方案 A:自建 API Key 体系(推荐)** ```python # 我们的 API Key 设计 def generate_api_key(user_id, platform_id): # Key 结构:{platform_prefix}{version}{user_hash}{checksum} # platform_prefix: 我们的平台标识(如 "LGW") # user_hash: 用户ID的哈希 # checksum: CRC32/MD5 校验 key = f"lgw_{version}_{user_hash}_{checksum}" return key def verify_api_key(key): # 1. 验证格式 # 2. 验证平台标识(我们的平台) # 3. 验证校验和 # 4. 验证是否在我们的数据库中 if not key.startswith("lgw_"): return False # 不是我们的 Key # 继续验证... return True ``` **方案 B:使用 Token 代替 API Key** - 不直接传递 API Key - 使用 OAuth 2.0 风格的 Access Token - Token 绑定到具体部署,无法跨部署使用 --- ## 3. 我们的 API Key 设计规范 ### 3.1 Key 结构 ``` {LGW}-{版本}-{用户哈希}-{时间戳}-{随机数}-{校验和} 示例: lgw-v1-u7f3a2b1-t1700000000-r8f3a2-e9d4c1b2 ``` | 字段 | 说明 | 长度 | |------|------|------| | LGW | 平台标识 | 3 | | v1 | 版本号 | 2 | | u7f3a2b1 | 用户哈希 | 8 | | t1700000000 | 时间戳 | 10 | | r8f3a2 | 随机数 | 6 | | e9d4c1b2 | 校验和 | 8 | ### 3.2 验证流程 ``` 收到 API Key 请求 │ ▼ ┌─────────────────┐ │ 1. 格式验证 │ ──▶ 格式错误 → 400 └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 2. 平台标识 │ ──▶ 不是 "lgw-" → 401 └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 3. 校验和验证 │ ──▶ 校验失败 → 401 └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 4. 数据库验证 │ ──▶ Key 不存在/已禁用 → 401 └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 5. 权限验证 │ ──▶ 无权限 → 403 └────────┬────────┘ │ ▼ 验证通过 ``` ### 3.3 激活码设计 ``` {LGW}-{类型}-{用户ID}-{过期时间}-{随机数}-{校验和} 示例: lgw-act-1000-20260331-r8f3a2-e9d4c1b2 ``` | 字段 | 说明 | |------|------| | lgw | 平台标识 | | act | 激活码类型 | | 1000 | 用户ID | | 20260331 | 过期日期 | | r8f3a2 | 随机数 | | e9d4c1b2 | 校验和 | --- ## 4. 技术实现 ### 4.1 Key 生成服务 ```python import hashlib import secrets import time class APIKeyGenerator: PLATFORM_PREFIX = "lgw" VERSION = "v1" @classmethod def generate(cls, user_id: int) -> str: # 用户哈希(8位) user_hash = hashlib.md5(str(user_id).encode()).hexdigest()[:8] # 时间戳(10位) timestamp = str(int(time.time())) # 随机数(6位) random = secrets.token_hex(3)[:6] # 组合 raw = f"{cls.PLATFORM_PREFIX}-{cls.VERSION}-{user_hash}-{timestamp}-{random}" # 校验和(8位) checksum = hashlib.md5(raw.encode()).hexdigest()[:8] return f"{raw}-{checksum}" @classmethod def verify(cls, key: str) -> bool: # 1. 格式验证 parts = key.split("-") if len(parts) != 6: return False # 2. 平台标识验证 if parts[0] != cls.PLATFORM_PREFIX: return False # 3. 校验和验证 raw = "-".join(parts[:5]) expected_checksum = hashlib.md5(raw.encode()).hexdigest()[:8] if parts[5] != expected_checksum: return False # 4. 数据库验证(在 Controller 中实现) return True ``` ### 4.2 激活码生成服务 ```python class ActivationCodeGenerator: PLATFORM_PREFIX = "lgw" CODE_TYPE = "act" @classmethod def generate(cls, user_id: int, expiry_days: int) -> str: # 计算过期日期 expiry = datetime.now() + timedelta(days=expiry_days) expiry_str = expiry.strftime("%Y%m%d") # 随机数 random = secrets.token_hex(3)[:6] # 组合 raw = f"{cls.PLATFORM_PREFIX}-{cls.CODE_TYPE}-{user_id}-{expiry_str}-{random}" # 校验和 checksum = hashlib.md5(raw.encode()).hexdigest()[:8] return f"{raw}-{checksum}" ``` --- ## 5. 数据库设计 ```sql -- API Keys 表 CREATE TABLE api_keys ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, key_hash VARCHAR(64) NOT NULL UNIQUE COMMENT 'Key 的哈希(用于查询)', key_prefix VARCHAR(20) NOT NULL COMMENT 'Key 前缀(用于展示)', -- 绑定信息 team_id BIGINT, organization_id BIGINT, -- 权限 permissions JSON COMMENT '权限列表', allowed_models JSON COMMENT '允许的模型列表', allowed_ips JSON COMMENT 'IP 白名单', -- 限制 rate_limit_rpm INT DEFAULT 60, rate_limit_tpm INT DEFAULT 100000, max_concurrent INT DEFAULT 10, -- 状态 status VARCHAR(20) DEFAULT 'active' COMMENT 'active/disabled/expired', -- 时间 expires_at TIMESTAMP, last_used_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 审计 created_by BIGINT, ip_address VARCHAR(45), description VARCHAR(200), INDEX idx_user_id (user_id), INDEX idx_key_hash (key_hash), INDEX idx_status (status), INDEX idx_expires_at (expires_at) ) COMMENT 'API Keys 表'; -- 激活码表 CREATE TABLE activation_codes ( id BIGINT PRIMARY KEY AUTO_INCREMENT, code_hash VARCHAR(64) NOT NULL UNIQUE COMMENT '激活码哈希', code_prefix VARCHAR(20) NOT NULL COMMENT '激活码前缀', -- 绑定信息 user_id BIGINT NOT NULL, target_type VARCHAR(20) COMMENT '激活目标类型: subscription/package', target_id BIGINT COMMENT '激活目标ID', -- 状态 status VARCHAR(20) DEFAULT 'unused' COMMENT 'unused/used/expired', used_at TIMESTAMP, used_by BIGINT COMMENT '使用者ID', -- 时间 expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_code_hash (code_hash), INDEX idx_user_id (user_id), INDEX idx_status (status), INDEX idx_expires_at (expires_at) ) COMMENT '激活码表'; ``` --- ## 6. 与 Subapi 集成时的处理 ### 6.1 方案:我们的 Gateway 作为唯一入口 ``` 用户请求 │ ▼ 我们的 Gateway(验证 Key 来源) │ ├── 我们的 Key → 处理 │ └── Subapi 格式的 Key → 拒绝或转发到 Subapi ``` ### 6.2 API Key 识别逻辑 ```python def identify_key_type(key: str) -> str: if key.startswith("lgw-"): return "own" # 我们的 Key elif key.startswith("sk-"): return "openai" # OpenAI 原始 Key else: return "unknown" # 未知类型 ``` ### 6.3 流量分离 | Key 类型 | 处理方式 | |----------|----------| | `lgw-` 开头 | 我们的 Gateway 处理 | | `sk-` 开头 | 直接转发到对应供应商 | | 其他 Subapi 格式 | 转发到 Subapi(如果有集成) | --- ## 7. 风险评估与缓解 ### 7.1 风险评估 | 风险 | 影响 | 可能性 | 严重性 | |------|------|--------|--------| | Subapi Key 串用 | 计费损失/账号盗用 | 高 | 严重 | | 激活码伪造 | 权益被盗用 | 中 | 高 | | Key 泄露 | 未授权使用 | 高 | 高 | ### 7.2 缓解措施 1. **强制 Key 来源验证** - 所有 Key 必须包含平台标识 - 验证时必须查询数据库 2. **Key 轮换** - 定期轮换 Key - 用户可手动轮换 3. **使用监控** - 记录 Key 使用情况 - 异常使用告警 4. **IP 限制** - 支持 IP 白名单 - 异常 IP 告警 --- ## 8. 结论 1. **Subapi 存在严重安全漏洞**:API Key 不验证来源,可在任意部署使用 2. **我们的系统必须自建 Key 体系**: - Key 必须包含平台标识 - 必须数据库验证 - 必须防伪造 3. **集成时流量分离**: - 我们的 Key 由我们处理 - Subapi Key 转发到 Subapi --- **文档状态**:安全漏洞分析 **关联文档**: - `supply_detailed_design_v1_2026-03-18.md`