docs: settings 安全加固+UX迭代 changelog
This commit is contained in:
@@ -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 逻辑)
|
||||
Reference in New Issue
Block a user