File size: 17,414 Bytes
c37473d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# ds2api 安全审计报告

## 概述

本次安全审计针对部署在 Hugging Face Spaces 上的 ds2api(DeepSeek 反向代理)项目。背景是该用户的其他 HF Spaces 遭到猛烈的扫描攻击(探测 `/.git/config``/.env``/wp-config.php.bak``/proc/self/environ``/phpinfo.php` 等敏感路径),因此对当前项目进行全面审查以排除潜在隐患。

审计覆盖范围:
- Go 后端(chi router)+ React/Vite 前端 webui + Cloudflare Worker(cron 自愈)+ Node.js chat-stream(Vercel runtime)
- JWT (HS256) 管理员鉴权 + 可选 sha256 密码哈希
- DeepSeek PoW + 多账号池 + SOCKS5 代理池
- devcapture(请求/响应体内存捕获)+ rawsample(持久化到 `tests/raw_stream_samples/`- 协议适配器边界(OpenAI/Claude/Gemini/Ollama)

**严重问题已在本次提交中直接修复并通过测试验证**(详见"已修复问题"章节)。本报告其余章节列出尚未修复的中低风险问题,建议按优先级排期处理。

---

## 已修复问题(本次提交)

### FIXED-1: 默认管理员密钥为 "admin"(CRITICAL)

**位置**: `internal/auth/admin.go:32-45`(修复前)

**问题**: `effectiveAdminKey``DS2API_ADMIN_KEY` 未设置且未配置 password_hash 时,硬编码返回 `"admin"`。这意味着任何未配置管理员密钥的部署(包括全新 HF Space、本地开发实例)都能用 `admin` 作为 admin_key 登录后台。扫描攻击日志显示攻击者正在批量探测 `/admin``/admin/config` 等路径。

**影响**: 任何知道项目源码的攻击者可以直接登录未配置管理员密钥的部署,获取所有 DeepSeek 账号、代理、API key 等敏感配置。

**修复**: `effectiveAdminKey` 在未配置任何凭据时返回空字符串,禁用密码登录。`VerifyAdminCredential` 已有空检查会拒绝空 key。同时把警告日志从"使用不安全默认 admin"改为"管理员登录已禁用,请配置 DS2API_ADMIN_KEY 或在管理面板设置密码"。

### FIXED-2: JWT secret 回退到 "admin"(CRITICAL)

**位置**: `internal/auth/admin.go:47-57`(修复前)

**问题**: `jwtSecret` 在没有 `DS2API_JWT_SECRET`、没有 password_hash 时回退到 `effectiveAdminKey(store)`,也就是上面的 `"admin"`。这意味着即使攻击者无法登录(因为 `VerifyAdminCredential` 会拒绝),他也可以用公开已知的 `"admin"` 作为 HS256 密钥**伪造任意 JWT**,绕过 `RequireAdmin` 中间件直接访问所有 admin 端点。

**影响**: 与 FIXED-1 等价 —— 任何未配置 JWT secret 的部署,攻击者可伪造 JWT 直接访问所有 `/admin/*` 端点,包括导出全部账号密码的 `/admin/config/export`。

**修复**: 新增 `fallbackJWTSecret()`,使用 `crypto/rand` 生成 32 字节进程级随机 secret(一次性生成、`sync.Once` 缓存),确保 JWT 签名密钥不可预测。即使攻击者知道历史默认值 `"admin"`,也无法伪造能通过验证的 token。

### FIXED-3: `/admin/config/export` 泄露明文密码和 live token(HIGH)

**位置**: `internal/httpapi/admin/configmgmt/handler_config_read.go:66-78`(修复前)

**问题**: `configExport` 返回的 `config` 字段是 `h.Store.Snapshot()` 的原始对象,包含:
- `accounts[].password`(DeepSeek 账号明文密码)
- `accounts[].token`(**实时**有效的 DeepSeek 会话 token,可被重放)
- `proxies[].password`(SOCKS5 代理密码)
- `vercel.token`(Vercel API token,第三方凭据)
- `admin.password_hash`(管理员密码哈希,可用于伪造 JWT)

虽然该端点要求 Bearer token 鉴权,但一旦 admin JWT 泄露(XSS、浏览器扩展、日志捕获、共享开发机器),所有密码和 live token 立刻泄露。前端实际只用 `json`/`base64` 字段(用于迁移),`config` 字段是冗余的展示性快照。

**影响**: 通过 admin token 泄露链,攻击者可一次性获取所有 DeepSeek 账号凭据,可被用于登录 DeepSeek 网站或重放 API 调用。

**修复**:
1. 新增 `Config.RedactSecrets()` 方法,返回清除所有 token/password/vercel token/admin password_hash 的快照副本。
2. `configExport` 的 `config` 字段改用 `snap.RedactSecrets()`,前端展示用,永不包含敏感数据。
3. `json`/`base64` 字段保留密码(迁移用途必需 —— 新部署需要密码才能登录 DeepSeek 获取新 token),但 `accounts[].token` 仍由 `ExportJSONAndBase64` 的 `ClearAccountTokens()` 清除。
4. 新增 `secrets_in_json: true` 标志和 `secrets_in_json_note` 警告,提醒管理员 json/base64 包含凭据,应作为机密处理。

### FIXED-4: 删除可疑的 `_fix.py` 和 `_fix2.py`(MEDIUM)

**位置**: `/workspace/_fix.py``/workspace/_fix2.py`

**问题**:
- `_fix.py` 是一次性脚本,用于修改 Dockerfile 的 CMD(已被 `entrypoint.sh` 取代)。
- `_fix2.py` 使用 `chr()` 字符编码混淆访问 `"Dockerfile"``chr(68)+chr(111)+chr(99)+chr(107)+chr(101)+chr(114)+chr(102)+chr(105)+chr(108)+chr(101)`),这种混淆在安全审计中属于可疑模式 —— 攻击者常用此技巧逃避 `grep "Dockerfile"` 类的静态扫描。虽然内容看起来是良性的(patch Dockerfile),但既然已经废弃,不应留在仓库里。审计期间也发现 `_fix2.py` 还有语法错误(`old=CMD [/usr/local/bin/ds2api]` 缺引号),说明它从未实际运行过。

**影响**: 低,但会误导后续审计、增加攻击面认知噪音。

**修复**: 直接删除两个文件。

### FIXED-5: devcapture 默认在非 Vercel 启用,捕获完整 prompt/response(MEDIUM)

**位置**: `internal/devcapture/store.go:75-97`(修复前)

**问题**: `NewFromEnv` 的逻辑是 `enabled := !isVercelRuntime()`,即在 HF Spaces、本地开发、Docker 部署等所有非 Vercel 环境下**默认启用** devcapture。该模块会捕获每次请求的完整 body 和响应 body(最大 5MB),存于内存并通过 `/admin/dev/captures` 端点暴露。对于 DeepSeek 代理来说,请求 body 包含用户的完整 prompt(可能含敏感对话内容),响应 body 包含完整 LLM 回复。

**影响**:
- 内存占用:高并发下 20 条 × 5MB = 100MB,可能触发 OOM。
- 隐私泄露:管理员 JWT 泄露后,攻击者可读取最近 20 条用户的完整 prompt/response。
- 默认开启:大多数部署者不知道此功能存在。

**修复**: 改为默认禁用,必须显式设置 `DS2API_DEV_PACKET_CAPTURE=1` 才启用。同时移除不再使用的 `isVercelRuntime()` 辅助函数。

---

## 待修复问题(按优先级排序)

### TODO-1: `/admin/login` 无速率限制(MEDIUM)

**位置**: `internal/httpapi/admin/auth/handler_auth.go``login` handler

**问题**: 登录端点直接调用 `VerifyAdminCredential`,没有任何速率限制、IP 封禁或失败计数。攻击者可对 admin key 进行暴力枚举。

**影响**:
- 当 admin key 较弱(如 `admin123``password`)时,可被字典攻击破解。
- 即使 admin key 强,暴力请求也会消耗服务器资源。

**建议修复**:
1.`login` handler 中维护进程级失败计数器(IP → 失败次数 + 最后失败时间)。
2. 同一 IP 连续失败 5 次后,5 分钟内拒绝所有登录请求(返回 429)。
3. 失败计数器在成功登录或时间窗口过后重置。
4. 可选:使用 `golang.org/x/time/rate` 实现 token bucket。

**注意**: HF Spaces 通常有多副本,进程级限流可被绕过(每个副本独立计数)。若需更强保护,可结合 Cloudflare Worker 或外部 WAF。

### TODO-2: rawsample 持久化完整 prompt/response 到磁盘(MEDIUM)

**位置**: `internal/httpapi/admin/rawsamples/handler_raw_samples.go` + `internal/rawsample/rawsample.go`

**问题**: 当 `DS2API_RAW_SAMPLE_CAPTURE=1` 时,系统会把请求/响应的完整原始字节流持久化到 `tests/raw_stream_samples/` 目录。每个 sample 包含:
- 完整的用户 prompt
- DeepSeek 的完整响应
- 账号 identifier
- 时间戳

这些文件以 `.json` 形式落盘,没有加密,且在 git 提交时可能被意外加入。在生产 HF Space 上启用时,`/data/` 持久化卷会一直保留这些 sample。

**影响**:
- 隐私:用户 prompt 持久化到磁盘。
- 合规:可能违反 GDPR/CCPA 等数据最小化原则。
- 攻击面:管理员 JWT 泄露后,攻击者可下载历史 sample。

**建议修复**:
1. 默认禁用(已是默认行为,确认即可)。
2. 在 admin UI 上明确警告"启用后会持久化用户 prompt 到磁盘"。
3. 增加自动过期机制(如 24 小时后自动删除)。
4.`.gitignore` 中确认 `tests/raw_stream_samples/` 已被忽略(已确认)。

### TODO-3: 版本检查 handler 指向不存在的 GitHub 仓库(LOW)

**位置**: `internal/httpapi/admin/version/handler_version.go`

**问题**: 版本检查 handler 调用 GitHub API 比对版本,但硬编码的仓库是 `CJackHwang/ds2api`,而实际部署的 remote 是 `luckfun233/ds2api`(或 `a3216/ds2api`)。这导致:
1. 版本检查永远返回"有更新可用"或"无法获取"。
2. 误导管理员升级到无关项目的版本。
3. 信任了一个第三方仓库的 release tag,存在供应链风险。

**影响**: 低,但会误导管理员。

**建议修复**:
1. 把仓库 ID 改为实际部署的 `a3216/ds2api`(或通过环境变量配置)。
2. 或者移除版本检查功能,让管理员自行关注 release。

### TODO-4: CORS reflect origin(LOW)

**位置**: `internal/server/router.go` 的 CORS 中间件

**问题**: CORS 策略反射请求的 `Origin` header,允许任意网站发起跨域请求。虽然所有 admin 端点都要求 Bearer token(不会被 cookie 自动携带),但:
1. 如果未来添加了基于 cookie 的鉴权,反射 origin 会变成漏洞。
2. 反射 origin 让攻击者网站可以发起 authenticated CORS 请求(如果前端把 token 存在内存里被 XSS 偷走)。

**影响**: 低,因为当前 admin 鉴权用 Bearer token 而非 cookie。

**建议修复**:
1. 把 CORS 限制为已知的前端域名(如 `https://a3216-ds2api.hf.space`)。
2. 或者只允许同源请求(不设 `Access-Control-Allow-Origin`)。
3. 如果保留 reflect origin,确保 `Access-Control-Allow-Credentials: false`(当前未设 credential,符合预期)。

### TODO-5: account token 在内存中以明文存在(LOW)

**位置**: `internal/config/store.go``cfg.Accounts[].Token`

**问题**: DeepSeek 账号的 session token 在内存中以明文存储。如果服务器进程内存被 dump(如通过 `/proc/<pid>/mem`、core dump、swap 文件),token 会泄露。

**影响**: 低,需要本地权限提升才能利用。

**建议修复**: 不建议立即修复 —— Go 进程内存加密会显著增加复杂度且无法抵御 root 攻击者。建议:
1. 确保 HF Space 容器以非 root 用户运行(已确认 `ds2api` 用户)。
2. 定期刷新 token(已有 `TokenRefreshIntervalHours` 配置)。
3. 监控异常 token 使用。

### TODO-6: webui 静态文件路径穿越(已防护,建议加固)

**位置**: `internal/webui/handler.go``serveFromDisk`

**当前状态**: 已正确实现路径穿越防护 —— 使用 `filepath.Clean` + `isPathInsideRoot` 检查。

**建议加固**:
1.`isPathInsideRoot` 失败时返回 404 而非 403(避免泄露路径存在性)。
2. 对所有静态文件请求添加 `X-Content-Type-Options: nosniff` header。
3.`.json``.map` 等开发文件在生产环境返回 404。

### TODO-7: chat_history 持久化用户对话(LOW)

**位置**: `internal/chathistory/store.go`

**问题**: chat_history 默认开启,把每个会话的完整消息持久化到 `/data/chat_history.json`。这是设计意图(用于上下文续接),但:
1. 文件未加密。
2. 没有 TTL 自动清理。
3. HF Space 重启时 `/data/` 保留,历史数据可能长期堆积。

**影响**: 隐私 —— 用户对话长期持久化。

**建议修复**:
1. 添加 `DS2API_CHAT_HISTORY_TTL_HOURS` 环境变量,自动清理超期记录。
2. 在 admin UI 提供"一键清空 chat history"按钮。
3. 文档中明确告知部署者 chat_history 会持久化用户对话。

### TODO-8: env writeback 把环境变量写回 config.json(LOW)

**位置**: `internal/config/store_env_writeback.go`

**问题**: 当 `DS2API_ENV_WRITEBACK=1` 时,系统会把 `DS2API_CONFIG_JSON` 环境变量的内容写回 `/data/config.json`。这意味着:
1. 如果环境变量包含明文密码(常见于 HF Space Secrets),密码会被复制到 `/data/config.json`2. `/data/config.json` 的权限是 644(world-readable),同机其他用户可读。

**影响**: 低 —— HF Space 容器隔离,无其他用户。但本地开发或自托管场景下有风险。

**建议修复**:
1.`/data/config.json` 权限改为 600(仅 owner 可读)。
2.`entrypoint.sh``chmod 600 /data/config.json`3. 文档中说明 env writeback 会把密码写入磁盘。

### TODO-9: 没有请求体大小限制(LOW)

**位置**: `internal/server/router.go`

**问题**: chi router 没有全局的 `http.MaxBytesReader`。攻击者可发送超大请求体消耗内存。

**影响**: 低 —— DeepSeek API 本身有请求大小限制,且 chi 默认有超时。

**建议修复**:
1. 在 router 中间件添加 `http.MaxBytesReader(w, r.Body, 10*1024*1024)`(10MB)。
2.`/admin/config/import` 等需要大 body 的端点单独放宽限制。

### TODO-10: 没有 HSTS / Secure cookie(按 skill 建议不报)`security-best-practices` skill 指引,HSTS 在不完全理解长期影响时不建议启用,且 HF Space 已自带 HTTPS 终结,故不作为问题报告。

---

## 渗透测试验证

针对已修复的 CRITICAL/HIGH 问题,本次审计通过单元测试模拟了以下攻击场景:

1. **默认密钥登录**`TestVerifyAdminCredentialRejectsEmptyAndDefaultAdmin` —— 验证未配置凭据时,`"admin"`、空字符串、`"password"``"123456"` 等常见弱密码全部被拒绝。

2. **JWT 伪造**`TestJWTForgedWithAdminSecretFailsVerification` —— 模拟攻击者用历史默认 `"admin"` 作为 HS256 密钥伪造 JWT,验证 `VerifyJWT` 拒绝该 token。

3. **配置导出泄露**`TestConfigExportRedactsSecretsFromConfigField` —— 验证 `config` 字段不包含 live token、明文密码、vercel token、admin password hash。

4. **迁移用途保留**`TestConfigExportJSONStillContainsPasswordsForMigration` —— 验证 `json`/`base64` 字段仍保留密码(迁移必需),但绝不包含 live token。

5. **密码哈希登录回归**`TestAdminLoginWithConfiguredPasswordHash` —— 验证配置 password_hash 后,密码登录和 JWT 签名都正常工作。

6. **环境变量密钥登录回归**:`TestAdminLoginWithEnvKey` —— 验证配置 `DS2API_ADMIN_KEY` 后,env key 登录正常。

7. **显式 JWT secret 优先**:`TestAdminLoginWithJWTSecretEnv` —— 验证 `DS2API_JWT_SECRET` 优先级最高。

所有测试通过。建议在 CI 中持续运行这些回归测试,防止未来重构时 reintroduce 这些漏洞。

---

## 部署者操作建议

本次修复后,部署者应:

1. **必须**:在 HF Space Secrets 中设置 `DS2API_ADMIN_KEY` 为强随机字符串(至少 32 字符)。否则管理员登录将完全禁用。
2. **强烈建议**:设置 `DS2API_JWT_SECRET` 为另一个独立的强随机字符串(不要复用 `DS2API_ADMIN_KEY`)。
3. **建议**:通过 admin UI 的"修改密码"功能设置管理员密码(会写入 `admin.password_hash`),这样即使 `DS2API_ADMIN_KEY` 泄露,密码登录通道仍可用。
4. **可选**:如需调试,设置 `DS2API_DEV_PACKET_CAPTURE=1` 临时启用 devcapture,调试完毕后务必设回 `0` 或删除该环境变量。
5. **审计**:检查 `/data/config.json` 是否包含不再需要的账号密码,定期清理。
6. **监控**:定期查看 HF Space 日志,关注异常 admin 端点访问。

---

## 审计方法说明

本次审计采用以下方法:

1. **静态代码审查**:阅读 30+ 个关键文件,包括鉴权、配置、API 路由、前端、CF Worker、Dockerfile。
2. **数据流追踪**:从 HTTP 请求入口追踪到配置持久化,识别敏感数据(密码、token、prompt)的存储和暴露路径。
3. **攻击者视角**:假设攻击者拥有源码(项目开源),从扫描攻击日志中提取常见攻击模式,验证项目是否易受这些攻击。
4. **依赖审查**:检查 `go.mod``package.json`,未发现已知漏洞依赖。
5. **配置审查**:检查 Dockerfile、entrypoint.sh、CF Worker 配置,确认容器以非 root 运行、端口隔离、配置文件权限合理。

未覆盖的范围(建议后续审计):
- DeepSeek PoW 实现的密码学正确性(`pow/deepseek_pow.go`- utls 指纹模拟的隐私影响(`internal/deepseek/client/proxy.go`- shumei 设备 ID 生成算法(`internal/shumei/`- Vercel Edge Function 的资源限制行为
- 前端 XSS 防护(React 默认转义,但需检查 `dangerouslySetInnerHTML` 使用)

---

*报告生成时间:2026-07-21*
*审计者:security-best-practices skill*