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 处问题
这个提交包含在:
2026-09-18 11:13:17 +08:00
父节点 3751dffef9
当前提交 f36149efc3
共修改 29 个文件,包含 1105 行新增和 558 行删除
+7 -13
查看文件
@@ -59,10 +59,8 @@
<label class="col">新密码<input name="new" type="password" autocomplete="new-password"></label>
<label class="col">确认新密码<input name="new2" type="password" autocomplete="new-password"></label>
<button class="btn primary" type="submit">修改密码</button>
<p class="hint">至少 {{ pwd_min }} 位,且需包含大写字母、小写字母、数字、符号中的至少两类,
且不能是常见弱口令。
<b>修改成功后其他设备上的登录会立刻失效</b>(本机这次会话保留,不必重新登录)——
「怀疑被盗所以改密码」才能真正生效。</p>
<p class="hint">至少 {{ pwd_min }} 位,需包含大写字母、小写字母、数字、符号中的至少两类。
修改成功后其他设备上的登录会立刻失效,本机会话保留。</p>
</form>
</section>
</div>
@@ -73,16 +71,14 @@
<span class="tag mute">数据可携带</span>
</div>
<p class="hint" style="margin-top:0">
打包下载<b>属于你账号</b>的所有内容:用量明细、采集历史、与你相关的操作审计、
以及账号信息与对你有效的配置。
<br><b>不含</b> Cookie 明文(只写「配没配、多少字符、什么时候更新的」)、
也不含其他任何账号的数据与实例级运行日志。
下载本账号的用量明细、采集历史、操作审计与有效配置。
不含 Cookie 明文,也不含其他账号的数据与实例级运行日志。
</p>
<div class="btnrow">
<a class="btn primary" href="{{ url_for('views.profile_export') }}">导出我的全部数据(zip)</a>
<a class="btn ghost" href="{{ url_for('views.records') }}">只导出用量明细(CSV)</a>
</div>
<p class="hint">导出动作按账号有最小间隔(10 秒)保护,避免脚本反复触发整表扫描。</p>
<p class="hint">两次导出之间至少间隔 10 秒。</p>
</section>
<section class="card">
@@ -105,10 +101,8 @@
</td></tr>
</table>
<p class="hint">
凭证以密文形式存在数据库里(主密钥在 <code>data/instance.json</code>),
页面与接口**任何时候都不回传明文**,只显示长度与尾部 4 位。要更换请到
<a href="{{ url_for('views.config_page') }}">配置管理</a>粘贴新的 Cookie 与 User-Agent
(两者必须取自同一次浏览器请求)。
凭证以密文入库(主密钥在 <code>data/instance.json</code>),页面与接口都不回传明文。
更换请到<a href="{{ url_for('views.config_page') }}">配置管理</a>重新粘贴 Cookie 与 User-Agent。
</p>
</section>