11 KiB
11 KiB
Nexus 安全巡检阶段 4:Settings/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仍有旧审计 labelreveal_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 当前一键登录链路
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 已修复的问题
当前代码已经解决:
tmp_token使用 86400 秒 TTL,而不是宝塔默认较短 TTL。- 登录 URL 不缓存,因为
tmp_token是一次性链接。 - 对同一台服务器生成登录 URL 时使用 Redis 锁,避免并发覆盖。
- 登录前会 best-effort patch 宝塔
/www/server/panel/task.py:把硬编码if f_time > 3600:改成读取public.get_session_timeout() or 86400。
2.3 仍可能导致“2 小时后刷新掉线”的关键点
当前 patch 的语义是:
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-tokenserver/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/setupserver/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_oksession_cleanup_ttl_patchedsession_timeout_secondswarnings
- 前端一键登录时提示“当前宝塔会话约 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/更新。
好处:
- 安全策略统一,不会每个接口散落实现。
- 后续审计更清晰。
风险:
- 改动较大,测试量更大。
- 前端交互也要统一。
推荐度:长期推荐,短期可分阶段做。
方案 G:refresh token 只允许 HttpOnly Cookie,不再接受 body fallback
改动:
/api/auth/refresh删除 body refresh_token fallback。- 如有历史客户端,先加配置开关或废弃日志。
好处:
- Token 传输路径更少。
- 更符合当前注释里的安全目标。
风险:
- 如果有脚本/旧前端依赖 body refresh,会受影响。
推荐度:中,先确认没有兼容需求再做。
5. 我建议的执行顺序
如果目标是先解决“宝塔 2 小时掉线”,建议:
- 先做方案 C 的诊断字段或 SSH 检测脚本,确认目标面板 session timeout 是否为 7200。
- 再做方案 A:自动把宝塔 session timeout 提升到 86400。
- 回归测试:同一台宝塔一键登录后刷新不掉;多台并发生成登录 URL 不互相覆盖。
- 然后再做 settings/auth 安全收口:优先方案 D 或 E。
如果目标是先收安全面,建议:
- 方案 D 或 E 处理 Telegram Token。
- 方案 F 分阶段统一敏感操作二次认证。
- 方案 G 作为兼容性确认后的增强项。
6. 待用户选择
请在以下方向里选一个或多个:
- A:自动强制宝塔 session timeout >= 86400,优先解决掉线。
- B:只检测告警,不自动改宝塔。
- C:一键登录返回会话诊断字段,前端提示原因。
- D:Telegram Token 像全局 API Key 一样完全不显示。
- E:Telegram Token 继续可显示,但必须当前密码二次认证。
- F:做统一敏感操作二次认证框架。
- G:refresh token 只允许 HttpOnly Cookie。