Skip to content

Commit 48c913a

Browse files
committed
docs(adr): ADR-017 安全加固批决策记录与豁免清单同步
1 parent 905b962 commit 48c913a

3 files changed

Lines changed: 189 additions & 47 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# ADR-017: 安全加固批(CSP 基线与白名单、opener 移除、compose 强制配置、nginx 安全头、.env.production 去跟踪)
2+
3+
## 状态
4+
5+
已接受
6+
7+
## 日期
8+
9+
2026-08-21
10+
11+
## 背景
12+
13+
三维复审(安全维度)对桌面端与服务端各暴露出一批同类问题:
14+
15+
1. **桌面端 CSP 缺基线**:tauri.conf.json 此前无 CSP(等同 `null`),任意脚本/连接/图片源不受约束。
16+
2. **opener 插件无用途却常驻**:`tauri_plugin_opener` 注册于 Builder 且 Cargo 依赖在列,但应用内无外链打开需求,徒增 IPC 攻击面。
17+
3. **compose 配置静默缺省**:`REDIS_PASSWORD`/`CORS_ORIGINS` 缺失时 compose 以空值/难读错误收场,生产可能带弱配置启动。
18+
4. **nginx 安全头继承陷阱**:nginx `add_header` 继承规则——location 一旦出现任何 `add_header` 就不再继承 server 层全部安全头,集中式声明会静默失效。
19+
5. **server/.env.production 曾入库**:生产环境变量文件被 git 跟踪,存在密钥泄露面(AGENTS.md §4 安全红线:明文存储敏感数据)。
20+
21+
## 决策
22+
23+
我们将按以下口径完成本批加固:
24+
25+
1. **桌面端 CSP 基线(tauri.conf.json)**:
26+
- `default-src 'self'`;`script-src 'self'`(不接受 unsafe-inline/eval);
27+
`style-src 'self' 'unsafe-inline'`(前端框架内联样式刚需);
28+
`media-src 'self' asset: http://asset.localhost blob:`(本地媒体回放);
29+
`font-src 'self' data:`;`connect-src` 白名单见下。
30+
- `connect-src` 域名清单依据(逐项对应真实网络出口,不多给):
31+
- `ipc:` / `http://ipc.localhost`:Tauri 2 IPC 通道(必需);
32+
- `ws:`:Vite dev 热更新(开发模式必需,生产无 ws 连接);
33+
- `https://api.siliconflow.cn`:云端 AI 增强网关端点(ADR-010 补缝式 AI,用户授权后才调用);
34+
- `https://hf-mirror.com`:流式 ASR/标点等模型下载镜像;
35+
- `https://www.modelscope.cn`:OCR 模型 ModelScope 自动缓存。
36+
- `img-src` 含 `https:`(三维复审 #11 修复):产品功能支持笔记外链图片
37+
(NoteImage.tsx 对 https 直出);此前 CSP 为 null 时外链图可显示,
38+
加固不得静默破坏既有功能。
39+
2. **opener 权限移除**:删除 `tauri_plugin_opener::init()` 注册与 Cargo 依赖;
40+
capabilities 不含 opener 权限;前端 `@tauri-apps/plugin-opener` 由前端侧同步移除。
41+
3. **compose `${VAR:?}` 强制配置**:`docker-compose.prod.yml` 对 `REDIS_PASSWORD`
42+
与 `CORS_ORIGINS` 使用 `${VAR:?...}` 强制插值(缺失即拒绝启动,禁止通配 `*` CORS);
43+
`server/deploy.sh` 在 CHANGE_ME 检查后提前 source 校验二者存在性,
44+
把 compose 插值阶段的难读错误提前为可读提示。
45+
4. **nginx 安全头片段化**:安全头收敛为 `server/nginx/conf.d/security_headers.conf`
46+
片段;每个自带 `add_header` 的 location 必须显式 `include` 该片段
47+
(nginx 继承规则决定无法只在 server 层声明一次),新增 location 同口径。
48+
5. **server/.env.production 去跟踪**:`.gitignore` 收录 `*.env.production`;
49+
文件以 `git rm --cached` 移出索引(保留磁盘文件);模板以
50+
`.env.production.example` 入库供部署参考。
51+
52+
## 备选方案
53+
54+
### 方案 A:CSP img-src 收紧为白名单域名(未采用)
55+
- 优点:外链图可精确控制,跟踪像素面更小。
56+
- 缺点:笔记外链图片来源不可枚举(用户粘贴任意图床/课程资料链接),白名单必然漏配,功能被静默破坏。
57+
- 适用场景:外链图来源封闭的产品。
58+
59+
### 方案 B:CSP img-src 放开 `https:`(采用)
60+
- 优点:与既有功能契约一致(CSP null 时代外链图可显示);https 限定排除 http 明文图。
61+
- 缺点:任意 https 图可加载,跟踪像素/外链探测面存在(笔记内容为用户自产,风险可控)。
62+
- 适用场景:用户生成内容含任意外链图的本地应用。
63+
64+
### 方案 C:nginx 安全头只声明在 server 层(未采用)
65+
- 优点:单点维护。
66+
- 缺点:任一 location 自带 add_header(缓存/CORS/X-Accel-Buffering 均有此需求)即整体失继承——安全头静默消失,比不配更危险(误以为有防护)。
67+
- 适用场景:无 location 级 add_header 的简单站点。
68+
69+
## 选择理由
70+
71+
- 桌面端是本地优先应用:网络出口少且可枚举,connect-src 用最小域名清单;
72+
img-src 放宽到 `https:` 是"加固不破坏功能"的显式取舍(复审 #11 闭环)。
73+
- 攻击面收窄优先于便利:无用途的 opener 插件直接移除,而非保留待用。
74+
- 服务端强制项用 `${VAR:?}` 显式失败而非静默缺省——生产部署宁可失败也不带弱配置。
75+
- nginx 片段化是对继承规则的如实应对,配合注释护栏防止新增 location 漏 include。
76+
77+
## 影响
78+
79+
### 正面影响
80+
- 桌面端具备 CSP 基线:脚本/连接源受控,IPC 与模型下载等真实出口逐一对应。
81+
- 生产部署硬门槛:密钥/CORS 缺失无法启动;deploy.sh 提供可读的前置校验。
82+
- server/.env.production 不再入库,密钥泄露面消除。
83+
84+
### 负面影响 / 代价
85+
- img-src `https:` 无法阻止任意 https 跟踪像素(接受:内容为本地用户自产)。
86+
- 新增 nginx location 必须记得 include 安全头片段(注释护栏 + 本 ADR 留档)。
87+
88+
### 风险
89+
- connect-src 白名单需随新网络出口同步更新(漏配表现为前端请求被 CSP 拦截,可诊断)。
90+
91+
## 合规性验证
92+
93+
- `tauri.conf.json` CSP 与本文清单逐项一致;新增网络出口需同步更新本 ADR 与 CSP。
94+
- `cargo build` 通过且无 `tauri-plugin-opener` 引用残留(Rust 侧;前端侧另一批移除)。
95+
- `bash -n server/deploy.sh` 语法校验通过;CHANGE_ME 与 REDIS_PASSWORD/CORS_ORIGINS
96+
缺失路径均有可读报错。
97+
- `git ls-files server/.env.production` 为空;`git check-ignore -v` 命中 `.gitignore` 规则。
98+
- nginx 容器启动后抽查任一带 add_header 的 location 响应头含 HSTS/X-Frame-Options/nosniff。
99+
100+
## 相关决策
101+
102+
- ADR-010: 补缝式 AI(云端增强需 connect-src api.siliconflow.cn)
103+
- ADR-016: AI 凭据存储方案(密钥不落明文——与 .env.production 去跟踪同红线)
104+
105+
## 参考
106+
107+
- server/docker-compose.prod.yml(`${VAR:?}` 强制项)
108+
- server/deploy.sh(前置校验与 Redis 健康检查 REDISCLI_AUTH 口径)
109+
- server/nginx/conf.d/security_headers.conf(安全头片段)
110+
- AGENTS.md §4 安全红线

‎docs/adr/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
| ADR-014 | [会话↔笔记关联与批量转化(notes.session_id 列 + SessionListItem 标记)](./ADR-014-session-note-association.md) | 已接受 | 2026-08-19 |
2222
| ADR-015 | [画面要点屏卡体系(screen_id/bbox 落库 + 屏聚合纯函数 + 派生屏视图)](./ADR-015-screen-cards-ocr.md) | 已接受 | 2026-08-20 |
2323
| ADR-016 | [AI 凭据存储方案(Windows DPAPI/keyring)](./ADR-016-ai-credentials-dpapi.md) | 已接受 | 2026-08-21 |
24+
| ADR-017 | [安全加固批(CSP 基线与白名单、opener 移除、compose 强制配置、nginx 安全头、.env.production 去跟踪)](./ADR-017-security-hardening-batch.md) | 已接受 | 2026-08-21 |
2425

2526
## 编号规则
2627

0 commit comments

Comments
 (0)