- Remove old review reports (keep latest only) - Move docs/ to deploy/docs-backup/ - Move performance-testing/ to deploy/ - Clean up test output files - Organize root directory
6.4 KiB
6.4 KiB
Sub2API 安全问题分析:激活码与API Key跨实例使用风险
问题描述
用户发现Sub2API系统生成的激活码(Redeem Code)和API Key在验证时未检查是否是本系统发放的,这意味着其他部署的Sub2API实例生成的激活码或API Key可能会在本系统中被接受和使用。
问题分析
1. 当前实现
1.1 激活码(Redeem Code)生成
// service/redeem_service.go - GenerateRandomCode
func (s *RedeemService) GenerateRandomCode() (string, error) {
// 纯随机生成,无系统标识
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("generate random bytes: %w", err)
}
code := hex.EncodeToString(bytes)
// 格式:XXXX-XXXX-XXXX-XXXX
return formatCode(code), nil
}
问题:激活码只是32位十六进制随机字符串,没有包含任何系统标识信息。任何Sub2API实例都能生成相同格式的激活码。
1.2 API Key生成
// service/api_key_service.go - GenerateKey
func (s *APIKeyService) GenerateKey() (string, error) {
// 格式:sk-{32位随机字符}
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return "sk-" + hex.EncodeToString(bytes), nil
}
问题:API Key也只是随机字符串,没有包含实例标识。
1.3 验证逻辑
// service/redeem_service.go - Redeem
func (s *RedeemService) Redeem(ctx context.Context, userID int64, code string) (*RedeemCode, error) {
// 只检查码是否存在,不检查来源
redeemCode, err := s.redeemRepo.GetByCode(ctx, code)
if err != nil {
if errors.Is(err, ErrRedeemCodeNotFound) {
return nil, ErrRedeemCodeNotFound
}
return nil, fmt.Errorf("get redeem code: %w", err)
}
// 检查状态和有效期
if !redeemCode.CanUse() {
return nil, ErrRedeemCodeUsed
}
// ... 兑换逻辑
}
验证流程:
- 数据库中查找激活码
- 检查状态是否为"unused"
- 检查是否过期
缺陷:没有验证激活码是否"由本系统生成"。
2. 影响范围
| 资源类型 | 受影响场景 | 风险等级 |
|---|---|---|
| 激活码 | 兑换余额/并发/订阅 | 高 |
| API Key | API访问 | 高 |
| 管理员API Key | 管理操作 | 极高 |
3. 攻击场景
场景1:跨实例激活码兑换
攻击者:
1. 在自己的Sub2API实例A生成激活码
2. 将激活码在受害者实例B兑换
3. 如果激活码未被使用,可能成功兑换
场景2:窃取API Key
攻击者:
1. 通过某种方式获取他人的API Key
2. 在自己的Sub2API实例使用该Key
3. 如果Key有效,可以访问受害者账户
解决方案
方案一:在Key中嵌入实例标识(推荐)
实现思路
在生成激活码和API Key时,嵌入系统实例的唯一标识(如实例ID、域名等)。
激活码格式改进
// 建议格式:{前缀}-{实例ID}-{随机}
const CodePrefix = "S2P" // Sub2API Prefix
func (s *RedeemService) GenerateCodeWithInstanceID(instanceID string) string {
// 实例ID(8位)
instancePart := fmt.Sprintf("%-8s", instanceID[:8])
// 随机部分(24位)
randomBytes := make([]byte, 12)
rand.Read(randomBytes)
randomPart := hex.EncodeToString(randomBytes)
return fmt.Sprintf("%s-%s-%s-%s",
CodePrefix,
instancePart,
randomPart[:8],
randomPart[8:])
}
验证逻辑
func (s *RedeemService) ValidateCode(code string) error {
parts := strings.Split(code, "-")
if len(parts) != 4 {
return ErrInvalidCodeFormat
}
// 验证前缀
if parts[0] != CodePrefix {
return ErrCodePrefixMismatch
}
// 验证实例ID
instanceID := parts[1]
if instanceID != s.instanceID {
return ErrCodeFromOtherInstance
}
// 继续验证其他逻辑...
}
API Key格式改进
// 建议格式:sk-{实例ID简称}-{随机}
const (
KeyPrefix = "sk"
InstanceShort = "s2p" // 简写
)
func (s *APIKeyService) GenerateKey() string {
randomBytes := make([]byte, 16)
rand.Read(randomBytes)
return fmt.Sprintf("%s-%s%s",
KeyPrefix,
InstanceShort,
hex.EncodeToString(randomBytes))
}
方案二:数据库隔离(替代方案)
实现思路
在数据库中为每个实例分配唯一标识,所有生成的激活码和API Key都关联到该标识。
实现
// 添加实例ID字段
type RedeemCode struct {
InstanceID string // 实例标识
Code string
// ...
}
// 生成时设置实例ID
func (s *RedeemService) GenerateCode() *RedeemCode {
return &RedeemCode{
InstanceID: s.config.InstanceID,
Code: generateCode(),
}
}
// 验证时检查实例ID
func (s *RedeemService) Redeem(ctx context.Context, code string) error {
dbCode, _ := s.redeemRepo.GetByCode(ctx, code)
if dbCode.InstanceID != s.config.InstanceID {
return ErrCodeFromOtherInstance
}
// ...
}
实施建议
1. 短期措施
- 记录实例标识:记录每个激活码/Key的生成实例(在日志中)
- 增强监控:监控来自异常位置的兑换请求
- 用户教育:提醒用户不要泄露自己的Key
2. 长期措施
- 方案一实施:在Key格式中嵌入实例标识
- 数据库迁移:为现有激活码/Key添加实例ID字段
- 过渡期:支持新旧格式并行,逐步迁移
3. 配置建议
# config.yaml
security:
# 实例标识(用于Key生成)
instance_id: "your-unique-instance-id"
# 是否启用实例验证(升级后启用)
validate_instance: true
风险评估
| 风险项 | 当前状态 | 影响程度 | 建议优先级 |
|---|---|---|---|
| 激活码跨实例使用 | 存在 | 高 | 高 |
| API Key跨实例使用 | 存在 | 高 | 高 |
| 管理员Key跨实例 | 存在 | 极高 | 紧急 |
总结
当前Sub2API系统在激活码和API Key生成时未包含系统标识,存在跨实例使用风险。建议在后续版本中:
- 短期:添加日志记录,便于追踪
- 长期:修改Key格式,嵌入实例标识
此问题需要开发团队评估影响范围后实施修复。
文档版本:1.0 最后更新:2025-01 分析基于:Sub2API v0.1.104