docs: settings 安全加固+UX迭代 changelog

This commit is contained in:
Your Name
2026-05-30 22:02:45 +08:00
parent a50f17941d
commit 1a61fae590
@@ -0,0 +1,83 @@
# Changelog — Settings 安全加固 + UX 迭代
**日期**: 2026-05-30
**Commit**: a50f179
**类型**: 安全修复 + UX 改进
**涉及文件**:
- `server/api/settings.py` — SSRF 防护 + key 白名单 + 值校验 + IP 审计
- `server/api/auth.py` — 密码修改要求 TOTP
- `server/application/services/auth_service.py` — _verify_totp staticmethod
- `web/app/settings.html` — UX 全面改进 + 密码修改重定向
- `web/app/login.html` — 密码修改后提示
## 安全修复 (5 项)
### S-01 SSRF 防护 (P0)
`POST /api/settings/ip-allowlist/parse-subscription` 可被利用探测内网。
- 新增 `_is_private_host()` 函数:IP 直检 + DNS 解析后二次校验
- 封禁:loopback/link-local/private/reserved IP 段
- `follow_redirects=False`,手动跟随最多 3 次重定向,每次检查目标
- 响应大小限制 1MB
### S-02 设置值校验 (P0)
`PUT /api/settings/{key}` 接受任意值。
- 新增 `MUTABLE_KEYS` 白名单(27 个已知 key),未知 key → 403
- 新增 `SETTING_VALIDATORS`:6 个 INT 设置项的类型+范围校验
- db_pool_size/db_max_overflow: 1-1000
- heartbeat_timeout: 10-600
- cpu/mem/disk_alert_threshold: 1-100
- 非整数值 → 400 "必须是整数",范围越界 → 400 "范围: X-Y"
### S-05 改密码后重登录 (P1)
密码修改成功后前端仅清空表单,用户留在 stale JWT 页面。
- 成功后跳转 `/app/login.html?msg=password_changed`
- login.html 显示绿色提示 "密码已修改,请重新登录"
### S-07 IP 操作审计 (P1)
`add_manual_ips``remove_allowlist_ip` 缺少审计日志。
- 两个端点添加 `request: Request` 参数
- 添加标准 AuditLog 写入(action/detail/ip_address
### S-09 改密码要求 TOTP (P1)
已启用 TOTP 的管理员改密码只需密码,可绕过 TOTP 保护。
- `ChangePasswordRequest` 新增 `totp_code: Optional[str]`
- `change_password` handler 检查:TOTP 已启用 + 未提供 code → 400
- `AuthService._verify_totp` 改为 `@staticmethod` 供 auth.py 调用
## UX 改进 (5 项)
### UX-01 输入框样式+类型
- 密码输入框添加 `focus:outline-none focus:ring-2 focus:ring-brand`
- 设置项 input type 映射:数值→`type="number" min="1"`URL→`type="url"`
- 所有动态渲染 input 统一添加 focus ring
### UX-02 改密码 loading 状态
- 按钮点击后 `disabled=true + textContent='提交中...'`
- 请求完成 `finally` 块恢复
### UX-03 加载失败 toast
- `loadSettings` / `loadTotpStatus` / `loadAllowlist` 的空 catch 改为 `console.error + toast`
### UX-04 保存失败回滚
- `saveSetting()` 失败时调用 `loadSettings()` 恢复原值
### UX-05 IP 格式校验
- 前端正则校验:IP (`x.x.x.x`) / CIDR (`x.x.x.x/n`) / 域名
- 后端 `IpAllowlistRequest` Pydantic `model_validator` 校验
## 验证结果
| 测试 | 结果 |
|------|------|
| SSRF localhost | ✅ 400 "不允许访问内网地址" |
| SSRF metadata | ✅ 400 "不允许访问内网地址" |
| 未知 key | ✅ 403 |
| INT 范围 99999 | ✅ 400 "范围: 1-1000" |
| INT 类型 "abc" | ✅ 400 "必须是整数" |
| 有效值 75 | ✅ 200 |
| IP 审计日志 | ✅ "添加 1 条手动 IP: 192.0.2.1" |
| TOTP schema | ✅ totp_code Optional 字段 |
| Input types | ✅ number/url/text 正确映射 |
## 是否需迁移/重启
- 需要重启后端服务(新增白名单+校验+SSRF 逻辑)