feat(安全): 对外暴露面加固 + 界面去 AI 化(v1.5.0)
界面(去 AI 味): - 大屏页清除 114 处生成器残留属性 data-page-node-id - 视觉系统改回工程控制台风格:去 radial/linear-gradient、去辉光、 去标题前彩色装饰条,改为中性灰阶 + 单一蓝色强调色;KPI 色条改状态点 - 精简各页说教式长提示;修掉 profile.html 泄漏到页面上的 Markdown 星号 - 删除登录页过时的「默认账号 admin / admin123」提示(1.4.0 起已无默认口令) 安全与隐私(按「将会被公网访问」收口): - 内部异常只回 8 位事件号,完整堆栈进服务端日志(web/api.py::_internal) - 导出文件名收敛:防响应头注入与路径穿越;manage.py passwd 补用户名校验 - 登录对不存在的账号也走一次哑哈希,抹平用户名枚举的时序差异 - /api/* 读接口限速 240 次 / 60 秒 / 账号(挡住循环调 /api/bundle) - 进程 umask 0077 + 目录 0700 / 文件 0600:对话正文与主密钥的落盘权限 - 表名与库文件路径只对管理员下发;大屏页所有数据插值转义 - --debug 只允许绑定回环地址;新增 Permissions-Policy 与 413 处理器 文档: - DEPLOYMENT 新增第十三节「安全与隐私基线」;迁移表补 1.4.0 → 1.5.0 行 - SECURITY 更新支持范围、新增「信息泄漏收敛」小节与上线检查项 - .codebuddy/ 加入 .gitignore(助手工作记忆不进仓库) 版本:1.4.0 → 1.5.0(无库结构变更,user_version 仍为 4) 验证:python tools/smoke.py → ok=264 fail=0;python tools/check_docs.py → 0 处问题
这个提交包含在:
+110
@@ -8,6 +8,116 @@
|
||||
|
||||
---
|
||||
|
||||
## [1.5.0] — 2026-09-18
|
||||
|
||||
**主题:按「将会被公网访问」重新审一遍界面与暴露面**
|
||||
|
||||
两件事:① 清掉界面里的 AI 生成残留与「AI 味」,把视觉从「霓虹渐变」改回工程控制台;
|
||||
② 按「能被公网访问」的前提逐条收口信息泄漏、注入与资源滥用。
|
||||
**没有库结构变更**(`user_version` 仍是 4),升级不需要手工介入。
|
||||
|
||||
### 安全与隐私加固(对外提供服务)
|
||||
|
||||
**主题:把「能被公网访问」当成前提,逐条收口信息泄漏与资源滥用**
|
||||
|
||||
这一轮没有新增功能,全部是收口。原则:**能把信息少给一点就少给一点,
|
||||
能把权限多收一层就多收一层**。
|
||||
|
||||
**信息泄漏(这类问题的共同点是:平时看不出问题,踩点阶段最好用)**
|
||||
|
||||
- **内部异常不再直出**。`/api/collect`、`/api/maintenance/*`、`/api/backups*` 的兜底
|
||||
`except Exception` 原本把 `str(e)` 原样回给客户端 —— 那里面会带绝对路径
|
||||
(`/app/workbuddy_portal/...`)、SQL 语句、`sqlite3` 的报错。现在统一走
|
||||
`web/api.py::_internal`:完整堆栈写进服务端日志并打一个 **8 位事件号**,
|
||||
客户端只拿到事件号。既不影响「出事了去查日志」,也不送材料。
|
||||
(`BadParam` / `Busy` / `NotReady` / `ApiError` / `BackupError` 这些**面向用户写的**
|
||||
业务异常不受影响,它们的文案本来就是给用户看的。)
|
||||
- **普通账号拿不到内部实现清单**。`/api/manifest`、`/api/bundle` 里的 `sources`
|
||||
(表名 `usage_records`、`daily 聚合视图` …)与 `archive`(`data/usage.sqlite`)
|
||||
只对管理员下发;非管理员的大屏页「数据源」卡片自动收起,避免留一个只有表头的空壳。
|
||||
- **响应头注入**。导出文件名里含用户名,而用户名**并不总是**注册正则的产物
|
||||
(`manage.py passwd` 建号时不校验、老库升级上来的名字也可能带引号或 CR/LF)。
|
||||
一旦名字里有引号或换行,直接拼 `Content-Disposition` 就是响应拆分。
|
||||
新增 `security.safe_filename()` 与 `security.content_disposition()`:
|
||||
收敛成 ASCII 安全名 + 按 RFC 5987 附上原名,`/records/export` 与
|
||||
`/profile/export` 都改走它。
|
||||
- **路径穿越**。`collect.export_csv()` 的账号名 tag 直接拼进文件路径 ——
|
||||
一个叫 `..\..\x` 的账号能把 CSV 写到数据目录之外。现在只保留 `[A-Za-z0-9._-]`,
|
||||
且结果固定落在 `EXPORT_DIR` 之下。同时 `manage.py passwd` **建号时补上用户名校验**
|
||||
(与注册页同一套),从根上不再产生这类名字。
|
||||
- **用户名枚举的时序侧信道**。`login_ok` 在账号不存在时不执行任何哈希计算,
|
||||
比「口令错误」快一到两个数量级,一个秒表就能枚举出哪些用户名真实存在。
|
||||
现在对不存在的账号也走一次**同代价的哑哈希**,两条路径耗时对齐。
|
||||
|
||||
**隐私:落盘权限(这条是对外部署最容易被忽略的)**
|
||||
|
||||
- 数据目录里躺着的是:`usage_records.prompt`(**用户与 AI 的完整对话正文**)、
|
||||
凭证密文、`instance.json`(主密钥)、`exports/*.csv`(对话正文的**明文**副本)、
|
||||
`backups/*.zip`(全库 + 主密钥)。默认 `umask 022` 会把它们留成 `0644 / 0755`,
|
||||
也就是**同机任何用户都能读**。
|
||||
- 新增 `config.harden_process()`:在进程启动时把 `umask` 收到 `0077`,
|
||||
此后新建的一切文件(含 SQLite 的 `-wal` / `-shm`)天然是 owner-only。
|
||||
刻意选「一条设置管全部」而不是逐个 `chmod` —— 逐点 `chmod` 一定会漏,
|
||||
而漏掉的那个文件里往往正好是最新的对话正文。
|
||||
- 配套 `harden_dir()` / `harden_file()` 收紧**已经存在**的目录(老版本留下的 0755)
|
||||
以及导出 CSV、备份 zip 落盘后的权限。Windows 无此模型,函数直接跳过。
|
||||
|
||||
**资源滥用**
|
||||
|
||||
- **读接口限速**:登录有 IP 锁定、注册有配额、采集有最小间隔,但读接口原本**没有任何刹车** ——
|
||||
一个注册账号把 F12 里的 `/api/bundle` 请求放进 `for` 循环就能持续吃 CPU 与出口带宽
|
||||
(那个接口要算全量逐日聚合,还会下发最多 2 万条明细)。现在 `/api/*` 按
|
||||
「账号(未登录时按来源 IP)」限速 240 次 / 60 秒,超出返回 `429`。
|
||||
阈值给得宽松:大屏切一次筛选只发 1~2 个请求,正常用户碰不到。
|
||||
|
||||
**其它**
|
||||
|
||||
- **`--debug` 不再能绑到对外地址**。Werkzeug 的交互式调试器等于任意代码执行,
|
||||
而 `--host` 默认就是 `0.0.0.0`;`python manage.py serve --debug` 会直接把 RCE 挂上公网。
|
||||
现在绑非回环地址直接拒绝启动,并提示本机调试的正确写法。
|
||||
- 新增 `Permissions-Policy` 响应头(显式关掉地理位置 / 麦克风 / 摄像头 / 支付 / USB)。
|
||||
- 新增 `413` 处理器:请求体超限时接口返回 JSON 而不是 Flask 默认 HTML 页
|
||||
(前端 `r.json()` 在 HTML 上会炸,表现成「Unexpected token <」)。
|
||||
- 文档:新增 [部署指南第十三节「安全与隐私基线」](DEPLOYMENT.md#十三安全与隐私基线) ——
|
||||
代码层已做到的、公网暴露前必须自己做的、存了哪些个人数据、保留期怎么定、上线自检命令。
|
||||
|
||||
验证:`python tools/smoke.py` **ok=264 / fail=0**(第 9 节专门覆盖上述各项:
|
||||
`safe_filename` 边界、真实导出响应头不含引号/换行、每个 `except Exception` 都走 `_internal`、
|
||||
读接口限速、安全响应头、大屏页已转义、普通账号拿不到数据源清单)。
|
||||
|
||||
### 界面去 AI 化
|
||||
|
||||
**主题:清掉界面里的 AI 生成残留与「AI 味」**
|
||||
|
||||
- **大屏页清理生成器残留**:`static/dashboard/index.html` 里 **114 处**
|
||||
`data-page-node-id="…"`(页面生成工具给每个节点打的标记)全部删除,
|
||||
文件从 64 KB 降到 58 KB。这类属性没有任何运行时作用,只是生成痕迹。
|
||||
- **视觉系统重做**(`static/css/app.css` + 大屏内联样式):由「深色底 + 霓虹渐变 + 辉光」
|
||||
改为中性的工程控制台风格 —— 灰阶打底、单一蓝色强调色。具体删掉了:
|
||||
`radial-gradient` 页面背景、按钮/激活态/分页/徽标的 `linear-gradient`、
|
||||
`box-shadow` 发光(品牌点、主按钮、日历格)、渐变文字(错误页大号状态码)、
|
||||
以及标题前的彩色装饰条(`.pagehead h1::before`、`.card h2::before`、`h1::before`)。
|
||||
- **KPI 改用灰度分层**:原来每张卡一条彩虹色左色条,现在 `--c` 只渲染成一个 8px
|
||||
状态点(`采集健康`/`自动备份` 这类状态卡仍能一眼区分),其余靠灰度与字重。
|
||||
- **图表配色同步降饱和**:ECharts `PALETTE`、日历 5 级色阶、热力图 `visualMap`
|
||||
与后台共用同一套语义色;数据系列改用蓝色阶 + 1 个琥珀色对比项。
|
||||
- **文案精简**:删掉说教式长段落(`配置管理` 页整张「为什么采集参数是只读的」说明卡)、
|
||||
把只给结论即可的提示压成一句话;`任务管理`/`配置管理`/`备份管理`/`用户管理`
|
||||
的长提示同步收敛。
|
||||
- **修掉两个真实缺陷**(都在这次清理中暴露):
|
||||
- `templates/profile.html` 的 Markdown 星号漏进 HTML —— `**任何时候都不回传明文**`
|
||||
会在页面上原样渲染成星号;
|
||||
- 登录页写着「首次部署默认账号 `admin` / `admin123`」,而 1.4.0 起已改用随机口令,
|
||||
该提示会把人引向一个永远登不上的口令,已删除。
|
||||
- **文档同步**:`docs/USER-GUIDE.md` 中引用上述被删文案的两处说明一并更新。
|
||||
界面截图 `docs/images/*.png` 仍是旧观感,需要用 `python tools/shots.py` 重出。
|
||||
|
||||
验证:`python tools/smoke.py` 全绿(ok=233 / fail=0),其中
|
||||
「页面 class 与 app.css 选择器对账」确认新版样式表没有漏掉任何模板在用的类名;
|
||||
`python tools/check_docs.py` 0 处问题。
|
||||
|
||||
---
|
||||
|
||||
## [1.4.0] — 2026-09-16
|
||||
|
||||
**主题:备份管理 · 对外提供服务的安全加固 · 资源与频率限制**
|
||||
|
||||
+109
-7
@@ -18,6 +18,7 @@
|
||||
- [十、日常巡检](#十日常巡检)
|
||||
- [十一、排错](#十一排错)
|
||||
- [十二、配置项速查](#十二配置项速查)
|
||||
- [十三、安全与隐私基线](#十三安全与隐私基线)
|
||||
|
||||
---
|
||||
|
||||
@@ -437,7 +438,7 @@ name: workbuddy-portal
|
||||
|
||||
services:
|
||||
portal:
|
||||
image: git.iwali.top/wangchuanli/workbuddy-portal:1.4.0
|
||||
image: git.iwali.top/wangchuanli/workbuddy-portal:1.5.0
|
||||
container_name: workbuddy-portal
|
||||
restart: unless-stopped
|
||||
init: true # tini 接管 PID 1,docker stop 能干净传到 python
|
||||
@@ -675,7 +676,7 @@ WB_TRUST_PROXY=1
|
||||
```bash
|
||||
export GITEA_TOKEN=<你的令牌>
|
||||
tools/push-all.sh # 推 main 分支 + 镜像 latest
|
||||
tools/push-all.sh 1.4.0 # 同时打一个版本 tag 并推送
|
||||
tools/push-all.sh 1.5.0 # 同时打一个版本 tag 并推送
|
||||
```
|
||||
|
||||
脚本做的事:
|
||||
@@ -713,15 +714,15 @@ docker login git.iwali.top -u wangchuanli
|
||||
|
||||
docker compose build
|
||||
docker tag git.iwali.top/wangchuanli/workbuddy-portal:latest \
|
||||
git.iwali.top/wangchuanli/workbuddy-portal:1.4.0
|
||||
git.iwali.top/wangchuanli/workbuddy-portal:1.5.0
|
||||
docker push git.iwali.top/wangchuanli/workbuddy-portal:latest
|
||||
docker push git.iwali.top/wangchuanli/workbuddy-portal:1.4.0
|
||||
docker push git.iwali.top/wangchuanli/workbuddy-portal:1.5.0
|
||||
```
|
||||
|
||||
### 7.4 验证远端
|
||||
|
||||
```bash
|
||||
docker manifest inspect git.iwali.top/wangchuanli/workbuddy-portal:1.4.0
|
||||
docker manifest inspect git.iwali.top/wangchuanli/workbuddy-portal:1.5.0
|
||||
# 或
|
||||
curl -s -u wangchuanli:TOKEN \
|
||||
https://git.iwali.top/api/v1/packages/wangchuanli?type=container
|
||||
@@ -944,8 +945,9 @@ sudo systemctl restart workbuddy-portal
|
||||
| **1.1.0 → 1.2.0**(单用户 → 多用户) | 0 → 2 | 建 `users` 表并写入首个管理员;`settings` / `usage_records` / `collect_runs` / `audit_log` 改为 `(user_id, …)` 复合主键,**老数据整体归到第一个账号**;明文 Cookie **就地加密**;建 `captchas` 表 | 不需要 |
|
||||
| **1.2.0 → 1.3.0**(权限收敛) | 2 → 3 | **配置作用域收敛**:把管理员个人名下的调度与采集参数提升到实例级 `user_id=0`,再清掉个人残留;实例级不再保留 `cookie` / `user_agent` | 不需要 |
|
||||
| **1.3.0 → 1.4.0**(备份 + 对外加固) | 3 → 4 | 只做两件事:`users` 加一列 `session_ver`(非空、默认 0)、建 `backups` 表(备份索引)。**没有数据搬运、没有键位变动** | 不需要 |
|
||||
| **1.4.0 → 1.5.0**(界面改版 + 公网加固) | 4 → 4 | **没有任何库结构变更**,`init_db()` 只做幂等校验。改动都在代码与界面上 | 不需要 |
|
||||
|
||||
三次迁移都是**幂等**的(可重复启动、可重复执行),各自会写一条审计:
|
||||
四次迁移都是**幂等**的(可重复启动、可重复执行),各自会写一条审计:
|
||||
|
||||
```bash
|
||||
docker compose logs portal | grep -iE "迁移|migrat|提升|promote"
|
||||
@@ -1285,7 +1287,7 @@ docker compose exec portal python manage.py users
|
||||
| 命令 | 作用 |
|
||||
|---|---|
|
||||
| `init [--user U] [--password P]` | 初始化 / 迁移数据库,创建首个管理员(幂等) |
|
||||
| `serve [--host H] [--port P] [--debug] [--no-scheduler]` | 启动 Web(含进程内调度) |
|
||||
| `serve [--host H] [--port P] [--debug] [--no-scheduler]` | 启动 Web(含进程内调度)。`--debug` **只允许绑回环地址**(Werkzeug 调试器可执行任意代码) |
|
||||
| `collect [-u U]` | 执行一次增量采集(不传 `-u` 则逐个启用账号) |
|
||||
| `migrate-csv [PATH] [-u U]` | 从 v1.0 的 CSV 导入(只读,可重复) |
|
||||
| `import-xlsx PATH [-u U]` | 从官网导出的 xlsx 导入 |
|
||||
@@ -1302,3 +1304,103 @@ docker compose exec portal python manage.py users
|
||||
| `restore <文件名> [--yes] [--no-instance]` | **从备份恢复**(整表替换)。**不加 `--yes` 只打印将要发生什么**;`--no-instance` = 不覆盖 `instance.json` |
|
||||
|
||||
Docker 部署下前面加 `docker compose exec portal`。
|
||||
|
||||
---
|
||||
|
||||
## 十三、安全与隐私基线
|
||||
|
||||
> 面向**要把这个实例挂到公网 / 开放注册**的部署者。
|
||||
> 代码层已经替你关掉的那部分不需要配置;需要**你决定**的部分在 13.2 与 13.4 节。
|
||||
|
||||
### 13.1 代码层已经做到的(不用配置,改坏了反而有风险)
|
||||
|
||||
| 威胁 | 处理方式 | 入口 |
|
||||
|---|---|---|
|
||||
| 撞库 / 爆破 | 图形验证码 + **按来源 IP 硬锁** + 用户名软退避 + 单 IP 尝试总量(40 次 / 5 分钟) | `security.py` |
|
||||
| 用户名枚举 | 口令校验对不存在的账号也走一次**哑哈希**,两条路径耗时对齐;登录失败文案统一 | `security.login_ok` |
|
||||
| 会话固定 / 劫持 | 登录时重建会话(顺带换掉 CSRF 与验证码 id);`HttpOnly` + `SameSite=Lax`;改密 / 重置 / 停用后 `session_ver` 立即作废旧会话 | `security.login_session` |
|
||||
| CSRF | 所有非 GET 请求在 `before_request` 里统一校验;退出登录也改成 POST | `security.check_csrf` |
|
||||
| XSS | Jinja 自动转义 + 前端手动 `esc()`(含 ECharts 的 HTML tooltip)+ CSP + `nosniff` | `security.CSP`、`dashboard/index.html` |
|
||||
| 点击劫持 | `X-Frame-Options: DENY` + CSP `frame-ancestors 'none'` | `apply_security_headers` |
|
||||
| 开放重定向 | `next=` 只允许站内相对路径 | `security.safe_next` |
|
||||
| **响应头注入** | 导出文件名一律先收敛成 ASCII 安全名,再按 RFC 5987 附上原名 | `security.content_disposition` |
|
||||
| **路径穿越** | 备份文件名走 `safe_name()`;导出 CSV 的账号名同样收敛(`manage.py passwd` 建号也补了用户名校验) | `backup.safe_name`、`collect.export_csv` |
|
||||
| **内部信息泄漏** | 兜底异常只回一个事件号(完整堆栈进服务端日志);普通账号拿不到表名与库文件路径 | `web/api.py::_internal`、`query.manifest` |
|
||||
| 资源耗尽 | 采集三道闸门(互斥 / 最小间隔 / 跨度上限)+ 重操作最小间隔 + **读接口限速(240 次 / 分钟 / 账号)** + 容器 `cpus`/`mem_limit`/`pids_limit` | `web/api.py`、`security.api_rate_ok`、`docker-compose.yml` |
|
||||
| 凭证泄漏 | Cookie 静态加密入库;页面与接口只回「长度 + 尾 4 位」;导出与备份都不含明文 | `crypto.py`、`db.get_secret` |
|
||||
| **落盘权限** | 进程 `umask 0077` + 目录 0700 / 文件 0600(SQLite 的 `-wal`/`-shm`、导出 CSV、备份 zip 一并覆盖) | `config.harden_process` |
|
||||
| 调试器 RCE | `--debug` 只允许绑定回环地址,绑对外地址直接拒绝启动 | `manage.py cmd_serve` |
|
||||
|
||||
### 13.2 公网暴露前你必须自己做的
|
||||
|
||||
1. **上 HTTPS,并把开关一起打开**(缺一个就是半截配置):
|
||||
|
||||
```
|
||||
WB_FORCE_HTTPS=1
|
||||
WB_COOKIE_SECURE=1
|
||||
# 反向代理终止 TLS 时还要:WB_TRUST_PROXY=1(且代理必须重写 XFF)
|
||||
```
|
||||
|
||||
见 [第六节](#六反向代理与-https)。
|
||||
|
||||
2. **直连公网时 `WB_TRUST_PROXY` 必须留 0**。置 1 的前提是「**你自己的**反代会重写
|
||||
`X-Forwarded-For`」;否则攻击者每换一个伪造的 XFF,验证码限速、注册配额、
|
||||
登录锁定三道 IP 防线会同时失效。
|
||||
3. **显式指定管理员口令**:`WB_ADMIN_PASSWORD=<强口令>`。留空时程序会生成随机口令并
|
||||
只在启动日志里打印一次,忘了抄就只能 `manage.py passwd` 重置。
|
||||
4. **决定要不要开放注册**:`allow_register`(默认 `1`)。开放就确认
|
||||
`captcha_policy=always` 与 `register_max_per_ip` 的取值。
|
||||
5. **别把备份卷和 `data/` 卷暴露出去**:备份 zip 里含**主密钥**(`instance.json`),
|
||||
拿到它等于拿到全库凭证的明文。备份目录默认不在 `data/` 内,容器里挂的是独立卷。
|
||||
6. **在应用前面再加一层限制**(可选但强烈建议):反代 / 云安全组做 IP 白名单或接 WAF。
|
||||
应用层的限速是最后一道,不该是唯一一道。
|
||||
|
||||
### 13.3 隐私:本系统存了哪些个人数据
|
||||
|
||||
| 数据 | 存在哪 | 谁能看到 | 备注 |
|
||||
|---|---|---|---|
|
||||
| **对话正文**(`User Prompt`) | `usage_records.prompt`;管理员导出时落到 `data/exports/*.csv` | 仅本人(页面 / 导出);管理员在**备份归档**里也能拿到 | 本系统里最敏感的一类数据。入库按 `max_prompt` 截断(默认 2048 字符) |
|
||||
| 云端凭证(Cookie / UA) | `settings.value`,**密文** | 仅本人,且只能看到「长度 + 尾 4 位」 | 主密钥在 `data/instance.json`(0600) |
|
||||
| 账号资料 | `users`(用户名 / 显示名 / 邮箱) | 本人 + 管理员 | 邮箱选填 |
|
||||
| 来源 IP | `users.register_ip`、`users.last_login_ip`、`audit_log.ip` | 仅管理员 | 用于每日注册配额与事后追责 |
|
||||
| 采集运行日志 | `collect_runs.detail`、`logs/app.log` | 本人(自己的);`/logs` 整页仅管理员 | 访问日志只记路径,**不记 query string** —— 否则 `?q=<搜索词>` 会把 prompt 片段带进日志文件 |
|
||||
|
||||
三条数据边界由**服务端**强制,不靠界面隐藏:
|
||||
|
||||
- 普通账号的一切读写都带 `user_id = 当前账号`;越权写入直接 `400`,并记一条
|
||||
`settings_rejected` 审计;
|
||||
- 管理员在「用户管理」看得到账号列表与登录 IP,但**看不到任何人的用量与凭证**;
|
||||
- 凭证明文只在「真正要拿它对外发请求」的那一刻解出来,不进日志、不进响应体。
|
||||
|
||||
### 13.4 保留期与删除:需要你自己定的部分
|
||||
|
||||
**代码刻意不做自动清理** —— 自动删数据比留数据危险得多。所以下面这些是部署者的事:
|
||||
|
||||
- `prompt` 与 `audit_log` 会一直留着。需要按时间裁剪时(**先 `manage.py backup`**):
|
||||
|
||||
```sql
|
||||
-- 例:清掉 180 天前的对话正文(保留行与积分,只去正文)
|
||||
UPDATE usage_records SET prompt='' WHERE day < date('now','-180 day');
|
||||
-- 例:清掉 365 天前的审计
|
||||
DELETE FROM audit_log WHERE at < date('now','-365 day');
|
||||
```
|
||||
|
||||
改完用 `manage.py vacuum` 回收空间。
|
||||
- **删除账号已经是「被遗忘权」的落地方式**:「用户管理 → 删除」默认**连同该账号的用量
|
||||
数据与 Cookie 一起删除**;用户自己可以走「个人中心 → 导出我的全部数据」把数据带走。
|
||||
- **导出文件的留存**:`data/exports/*.csv` 是对话正文的明文副本,没有谁会自动清。
|
||||
容器里它在 `wb_data` 卷内 —— 保留多久由你决定,别让它一直躺在磁盘上。
|
||||
|
||||
### 13.5 上线前的自检
|
||||
|
||||
```bash
|
||||
# 1) 应用层回归(第 9 节专门验「对外暴露面」:响应头收敛 / 异常不外泄 / 读接口限速)
|
||||
python tools/smoke.py
|
||||
|
||||
# 2) 从外部看到的响应头
|
||||
# 期望看到 CSP / Permissions-Policy / X-Frame-Options / nosniff / Referrer-Policy
|
||||
curl -sI https://你的域名/login | grep -iE 'content-security|permissions-policy|x-frame|nosniff|referrer'
|
||||
|
||||
# 3) 确认没有把调试器挂出去(这条应当直接报错退出,而不是启动成功)
|
||||
python manage.py serve --debug
|
||||
```
|
||||
|
||||
+2
-4
@@ -413,13 +413,11 @@ python manage.py import-creds -u alice # 想导给谁就写谁的用户名
|
||||
页面上、接口里都拿不到明文。若显示「**无法解密**」的红字横幅,说明实例主密钥被换过,
|
||||
重新粘贴一次即可。
|
||||
|
||||
> 凭证卡片上会有一行提示写着「**这一块是你唯一可以修改的配置**」——
|
||||
> 看到它就找对地方了。
|
||||
> 凭证卡片上会有一行提示写着「普通账号只有这一块可改」——看到它就找对地方了。
|
||||
|
||||
### 9.2 采集参数(只读)
|
||||
|
||||
普通账号在这一块看到的是**只读表格**,输入框不可编辑,页面上还有一张
|
||||
「**为什么采集参数是只读的**」说明卡:
|
||||
普通账号在这一块看到的是**只读表格**,输入框不可编辑:
|
||||
|
||||
| 参数 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
|
||||
在新工单中引用
屏蔽一个用户