2026-04-20-10-14-09 - 安全加固:XOR→AES-GCM、API Key环境变量化、授权码SHA-256哈希、消除硬编码密钥

This commit is contained in:
2026-04-20 10:27:17 +08:00
parent f0eda59c63
commit 31a2cdc77e
9 changed files with 608 additions and 43 deletions

View File

@@ -28,5 +28,45 @@
## 历史记录
(以下为实际踩坑记录,按时间倒序排列)
### [2026-04-20-10-14-09] Web Crypto API 异步性与 storage 同步接口冲突
### A. 具体问题
需求要求将 `systemSettings` 的加密从 XOR 升级为 Web Crypto API AES-GCM同时维持 `storage.get/set` 的同步调用签名。但 `crypto.subtle` 的所有加解密操作均为异步 Promise若直接将 `storage.get/set` 改为 async将导致 ReportEditor.tsx、Login.tsx 等数十处调用点需要同步改为 await改动面极大且风险不可控。
### B. 产生问题原因
Web Crypto API 是浏览器原生标准,设计为全异步 API无法像 crypto-js 那样提供同步调用。而项目早期设计时 `storage` 模块全部为同步接口,未预留异步扩展点。
### C. 解决问题方案
采用"启动时预解密 + 内存缓存"的折中方案:
1.`storage.ts` 中新增异步 `init()` 函数,在应用启动(`main.tsx``await storage.init()`)时预解密 `systemSettings` 到模块级内存缓存 `systemSettingsCache`
2. `storage.get('systemSettings')` 改为从缓存同步返回,`storage.set('systemSettings')` 改为更新缓存并触发异步持久化(不阻塞调用方)。
3. 其他 key 的读写保持原样,完全不受影响。
4. 该方案无需修改任何业务组件中 `storage.get('systemSettings')` 的调用方式。
### D. 后续如何避免问题
- 若未来再次遇到"必须引入异步存储操作"的需求,优先评估能否用"启动预加载 + 内存缓存"模式规避接口签名变更。
- 若必须改异步接口,应将影响范围限制在单一 key如新增 `getAsync/setAsync`),而非全局改动 `storage.get/set`
- 在需求分析阶段增加"接口兼容性影响评估"子项,提前识别同步/异步冲突。
---
### [2026-04-20-10-14-09] 旧数据兼容密钥与"消除硬编码"验收标准冲突
### A. 具体问题
安全加固的验收标准要求"构建产物中不再出现任何硬编码密钥字符串"。但实现 AES-GCM 迁移时,必须保留旧 XOR 密钥 `MedicalReportSys2024` 才能解密历史 localStorage 中的 `systemSettings` 数据。首次修改后构建产物扫描发现该字符串仍然存在。
### B. 产生问题原因
数据迁移需求(兼容旧 XOR 密文)与安全需求(消除硬编码密钥)表面上互斥。根源在于旧密钥是以明文常量形式写死在源码中的,构建产物必然包含该字符串。
### C. 解决问题方案
将旧 XOR 密钥同样移入构建阶段环境变量 `LEGACY_STORAGE_KEY`,通过 `vite.config.ts``define` 注入:
- 构建产物中不再包含固定的 `"MedicalReportSys2024"`
- 若部署方需要兼容旧数据,在构建时配置 `LEGACY_STORAGE_KEY="MedicalReportSys2024"` 即可正常迁移。
- 若部署方不需要兼容(全新部署),留空即可,旧数据将以 JSON.parse 失败处理,回退到 fallback。
### D. 后续如何避免问题
- 任何涉及历史数据迁移的安全重构,必须在方案设计阶段同步考虑"旧密钥/旧算法常量是否需要保留"。
- 检查清单:若验收标准包含"构建产物无硬编码敏感字符串",则所有历史兼容所需的常量也必须环境变量化。
- `.env.example` 中应显式标注 `LEGACY_STORAGE_KEY` 的用途("Former XOR encryption key"),避免运维人员误删。
---