Files
Nexus/docs/release/20260708/Nexus-安全巡检阶段4-settings-auth-btpanel-20260707.md
T
r 9b583ccc91
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / e2e (push) Blocked by required conditions
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
docs(release): 主与 release 分支合并功能清单 + 导入 release 文档目录
便于 Gitea 上 main ↔ release/20260708-axs 合并评审与冲突对照。
2026-07-09 01:14:32 +08:00

369 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Nexus 安全巡检阶段 4Settings/Auth 与宝塔长期会话边界审查
时间:2026-07-07
对象:测试机 `/opt/nexus-dev-current`,分支 `audit/security-review`
最新提交:`524acd5 fix-stop-exposing-global-api-key-in-settings`
> 本报告不输出任何密码、API Key、Token、Cookie、PEM 内容。
---
## 1. 本轮确认结果
### 1.1 全局 API Key 不显示:已完成代码层修复
只读检查结果:
- 后端 `/api/settings/api-key/reveal` 已删除。
- 前端 SettingsPage 中全局 API Key 显示/复制逻辑已删除。
- 源码残留仅有:
- `server/api/servers.py` 两处注释仍提到旧名字 `reveal_api_key`
- `frontend/src/utils/auditLabels.ts` 仍有旧审计 label `reveal_api_key: 查看 API 密钥`
安全判断:
- 残留注释/label 不会造成 API Key 暴露。
- 建议后续做一次小清理,避免误导维护者和审计日志翻译。
### 1.2 当前代码没有全局 API 限速
沿用上一份报告结论:
- 没有 slowapi / fastapi-limiter / nginx limit_req 一类全局限速。
- 429 主要来自:
- 管理员登录失败锁定:5 次 / 15 分钟。
- Agent 脚本回调:job 10 次 / 60 秒,IP 60 次 / 60 秒。
- 大多数“速度限制”是远程任务并发保护,不是 HTTP API 限速。
---
## 2. 宝塔 2 小时后刷新掉线:当前代码仍有一个关键边界
### 2.1 当前一键登录链路
```mermaid
sequenceDiagram
participant UI as Nexus 前端
participant API as /api/btpanel/servers/{id}/login-url
participant SVC as BtPanelService.create_login_url
participant Redis as Redis login_url_lock
participant SSH as ssh_bootstrap_panel
participant BT as 宝塔面板
UI->>API: 请求一键登录 URL
API->>SVC: create_login_url(server_id)
SVC->>Redis: 获取每台服务器分布式锁
SVC->>SSH: best-effort 检查/patch 宝塔 task.py session cleanup
SVC->>BT: config?action=set_temp_login(expire_time=now+86400)
BT-->>SVC: 返回 tmp_token 或登录 URL
SVC-->>UI: /login?tmp_token=...
```
### 2.2 已修复的问题
当前代码已经解决:
1. `tmp_token` 使用 86400 秒 TTL,而不是宝塔默认较短 TTL。
2. 登录 URL 不缓存,因为 `tmp_token` 是一次性链接。
3. 对同一台服务器生成登录 URL 时使用 Redis 锁,避免并发覆盖。
4. 登录前会 best-effort patch 宝塔 `/www/server/panel/task.py`:把硬编码 `if f_time > 3600:` 改成读取 `public.get_session_timeout() or 86400`
### 2.3 仍可能导致“2 小时后刷新掉线”的关键点
当前 patch 的语义是:
```python
session_cleanup_timeout = int(public.get_session_timeout() or 86400)
if f_time > session_cleanup_timeout:
删除 session 文件
```
这意味着:
- 如果宝塔面板里已经配置了 session timeout,例如 7200 秒(2 小时),patch 会尊重这个值。
- `or 86400` 只在没有配置值时生效。
- 所以用户遇到“打开 10 个宝塔,2 小时后陆续刷新掉线”,仍可能是目标宝塔的 `session_timeout` 本身就是 7200 秒。
结论:
> 现在的修复主要解决 `task.py` 硬编码 3600 秒清理问题,但不一定强制把宝塔会话生命周期提升到 24 小时。如果目标面板配置仍是 2 小时,会继续 2 小时后掉线。
### 2.4 并发打开 10 个宝塔的边界
当前 Redis 锁可以保证同一台服务器不会同时生成多个互相覆盖的 `tmp_token`。但用户说的是“同时打开 10 个不同宝塔”,这属于不同 server_id:
- 每台服务器独立锁,不互相等待。
- 各自的 session 生命周期取决于各自宝塔面板配置和 task.py patch 状态。
- 10 台里有的先掉、有的后掉,符合“每台服务器 session_timeout/task.py/定时清理状态不一致”的表现。
---
## 3. Settings/Auth 发现的问题与建议
### 3.1 中风险:Telegram Token reveal 不需要二次认证
位置:
- `server/api/settings.py` `/telegram/reveal-token`
- `server/api/settings.py` `/telegram/offline/reveal-token`
现状:
- 需要 JWT。
- 会写审计日志。
- 但不像密码预设 reveal、SSH Key reveal 那样要求当前密码二次认证。
风险:
- 如果管理员 access token 被盗,在 token 有效期内可直接读取 Telegram Bot Token。
- 虽然这是管理员功能,但与同文件中“密码预设/SSH 私钥 reveal 需要二次认证”的策略不一致。
建议:
- 若继续允许显示 Telegram Token,建议改成 `current_password` 二次认证。
- 如果你倾向于“全局 API Key 一样不显示”,也可以直接删除 Telegram Token reveal,只允许覆盖写入。
### 3.2 中风险:TOTP setup/enable 不要求当前密码
位置:
- `server/api/auth.py` `/totp/setup`
- `server/api/auth.py` `/totp/enable`
现状:
- 需要 JWT。
- 只允许对自己操作。
- enable 前会校验 TOTP code。
- 但不要求当前密码。
风险:
- 如果 access token 被盗,攻击者可以生成自己的 TOTP secret 并启用,造成账号被锁或持久化控制风险。
建议:
- setup 或 enable 至少其中一步要求当前密码。
- 更严格方案:setup、enable、disable、password change 都统一走“敏感操作二次认证”。
### 3.3 低风险:全局 API Key 相关审计 label/注释残留
位置:
- `server/api/servers.py` 注释里仍有 `same pattern as reveal_api_key`
- `frontend/src/utils/auditLabels.ts` 仍有 `reveal_api_key: 查看 API 密钥`
风险:
- 不暴露 secret。
- 但会误导后续开发,以为还有 reveal_api_key 功能。
建议:
- 后续清理为通用描述,例如 `same pattern as sensitive operation re-auth`
- 删除前端旧 label 或保留但标注 legacy。
### 3.4 低风险:refresh 接口仍接受 body refresh_token fallback
位置:
- `server/api/auth.py` `/api/auth/refresh`
现状:
- 优先读取 HttpOnly cookie。
- 没有 cookie 时允许从 body 读取 refresh_token。
风险:
- 当前注释解释为兼容 fallback。
- 如果没有明确的历史客户端需要,body fallback 增加了一条 token 传输路径。
建议:
- 保守:保留但加审计/废弃期。
- 严格:只允许 HttpOnly cookie refresh。
---
## 4. 下一步修改方向:至少 6 个方案供选择
> 按你的要求,涉及安全策略/代码改动前先给多个选项,不直接替你定。
### 方案 A:只修宝塔长期会话,强制把宝塔 session timeout 提升到 24 小时
改动:
-`ssh_bootstrap.py` 里增加写入/修正宝塔 session timeout 配置的逻辑。
- 登录前 best-effort 确保目标面板的 session timeout >= 86400。
- 保留现有 task.py patch。
好处:
- 最直接解决“2 小时后刷新掉线”。
- 不影响 Nexus 自身安全策略。
风险:
- 会修改宝塔面板自身配置。
- 如果用户希望宝塔短会话,这会改变预期。
是否需要重启/部署:
- 需要重建/重启 Nexus 后端容器。
- 对目标宝塔可能需要 reload bt。
推荐度:高,针对当前痛点最直接。
### 方案 B:宝塔 session timeout 只做检测和告警,不自动修改
改动:
- Nexus 登录前 SSH 检测宝塔当前 session timeout。
- 如果低于 86400,在 UI 或接口响应中返回 warning。
- 不自动改宝塔。
好处:
- 风险低,不擅自改目标面板。
- 可解释每台为什么 2 小时掉。
风险:
- 不自动解决问题,仍需要人工确认/点击修复。
推荐度:中。
### 方案 C:给宝塔登录 URL 接口增加“会话保活/会话诊断”返回字段
改动:
- `/api/btpanel/servers/{id}/login-url` 返回:
- `session_cleanup_ttl_ok`
- `session_cleanup_ttl_patched`
- `session_timeout_seconds`
- `warnings`
- 前端一键登录时提示“当前宝塔会话约 2 小时后失效”。
好处:
- 用户能看到问题原因。
- 便于排查 10 台里哪台会掉。
风险:
- 需要补前端显示。
- 只诊断,不一定修复。
推荐度:中高,适合和 A/B 搭配。
### 方案 D:敏感 Token 全部不显示,只允许覆盖写入
改动:
- 删除 Telegram Token reveal。
- 保留 token 是否已设置状态。
- 修改前端:显示“已设置,留空不改”,不提供查看/复制。
好处:
- 和“全局 API Key 不显示”策略一致。
- 降低 access token 被盗后的进一步泄露面。
风险:
- 管理员无法从 Nexus 找回 Telegram Token,只能重填。
推荐度:中高,安全更干净。
### 方案 E:保留 Telegram/密码/SSH reveal,但统一要求二次认证
改动:
- Telegram reveal 请求体增加 `current_password`
- 前端弹窗输入当前密码。
- 可以同时把 reveal 审计统一标准化。
好处:
- 兼顾可用性和安全。
- 与密码预设、SSH Key reveal 保持一致。
风险:
- 使用上多一步。
推荐度:高,如果你还想保留“查看 Telegram Token”。
### 方案 F:统一敏感操作二次认证框架
改动范围更大:
- TOTP setup/enable/disable。
- 修改密码。
- reveal 密码预设/SSH Key/Telegram Token。
- 宝塔 API Key 修改/显示类操作。
- 服务器凭据 reveal/更新。
好处:
- 安全策略统一,不会每个接口散落实现。
- 后续审计更清晰。
风险:
- 改动较大,测试量更大。
- 前端交互也要统一。
推荐度:长期推荐,短期可分阶段做。
### 方案 Grefresh token 只允许 HttpOnly Cookie,不再接受 body fallback
改动:
- `/api/auth/refresh` 删除 body refresh_token fallback。
- 如有历史客户端,先加配置开关或废弃日志。
好处:
- Token 传输路径更少。
- 更符合当前注释里的安全目标。
风险:
- 如果有脚本/旧前端依赖 body refresh,会受影响。
推荐度:中,先确认没有兼容需求再做。
---
## 5. 我建议的执行顺序
如果目标是先解决“宝塔 2 小时掉线”,建议:
1. 先做方案 C 的诊断字段或 SSH 检测脚本,确认目标面板 session timeout 是否为 7200。
2. 再做方案 A:自动把宝塔 session timeout 提升到 86400。
3. 回归测试:同一台宝塔一键登录后刷新不掉;多台并发生成登录 URL 不互相覆盖。
4. 然后再做 settings/auth 安全收口:优先方案 D 或 E。
如果目标是先收安全面,建议:
1. 方案 D 或 E 处理 Telegram Token。
2. 方案 F 分阶段统一敏感操作二次认证。
3. 方案 G 作为兼容性确认后的增强项。
---
## 6. 待用户选择
请在以下方向里选一个或多个:
1. A:自动强制宝塔 session timeout >= 86400,优先解决掉线。
2. B:只检测告警,不自动改宝塔。
3. C:一键登录返回会话诊断字段,前端提示原因。
4. DTelegram Token 像全局 API Key 一样完全不显示。
5. ETelegram Token 继续可显示,但必须当前密码二次认证。
6. F:做统一敏感操作二次认证框架。
7. Grefresh token 只允许 HttpOnly Cookie。