对应 5 份设计规格,共 75 个 Task: 1. 性能优化 (12 Task) — 批量INSERT/N+1内联name/合并COUNT/按需重绘/chunk拆分 2. 安全纵深防御 (8 Task) — RLS/行级数据范围/Redis session_key/审计哈希链 3. 事件驱动架构 (10 Task) — 11个缺失事件补发/LISTEN+NOTIFY/schema版本化 4. 前端工程化 (10 Task) — hook统一/组件拆分/Bundle优化 5. 可观测性运维 (10 Task) — 深度健康检查/Prometheus/OTel/生产Docker/告警
4.7 KiB
安全纵深防御实施计划
设计规格:
docs/superpowers/specs/2026-04-26-security-defense-in-depth-design.md日期: 2026-04-26 | 状态: draft | 总周期: 2-3 周
Phase 1: PostgreSQL RLS 安全网(Week 1)
Task 1: 创建 RLS 策略迁移
涉及文件: crates/erp-server/migration/src/m000073_enable_rls_all_tables.rs(新增), lib.rs
步骤: 对所有含 tenant_id 的表(30 基础 + 34 健康)执行 ALTER TABLE ENABLE ROW LEVEL SECURITY + CREATE POLICY tenant_isolation USING (tenant_id = current_setting('app.current_tenant_id')::uuid) + CREATE POLICY tenant_bypass USING (current_user IN ('erp_admin', 'erp_migration'))。创建 erp_app 数据库角色。down 方法完整回退。
验收: 迁移执行成功;以 erp_app 角色未设置 tenant_id 时查询返回空;现有应用行为不变。
Task 2: Axum middleware 设置当前连接的 tenant_id
涉及文件: crates/erp-server/src/middleware/tenant.rs
步骤: tenant 中间件从 JWT 解析 tenant_id 后执行 SET LOCAL app.current_tenant_id = '<id>'。确认在事务内执行(SeaORM 显式事务包装或 session 级 SET + RESET)。添加 tracing 日志记录注入状态。SET LOCAL 失败时 warn 但不阻断请求。
验收: 注入后以 erp_app 角色查询自动按 tenant_id 过滤;cargo test --workspace 通过。
Task 3: 验证现有测试不受影响
涉及文件: 可能修改测试辅助代码
步骤: 运行 cargo test --workspace 检查 RLS 导致的测试失败。分析失败原因(测试未设置 tenant_id → 查询返回空),在 TestDb/TestApp 初始化时注入 tenant_id。不修改任何业务逻辑代码。
验收: cargo test --workspace 全部通过。
Phase 2: 行级数据范围 + session_key Redis(Week 2)
Task 4: require_permission 增加 data_scope 过滤逻辑
涉及文件: erp-core/src/types.rs(TenantContext 增加 permission_data_scopes), erp-core/src/rbac.rs(apply_data_scope 函数), JWT 中间件, erp-health/src/handler/mod.rs
步骤: TenantContext 新增 permission_data_scopes: HashMap<String, DataScope>(枚举 All/Self/Department/DepartmentTree)。JWT 中间件查询 role_permissions.data_scope 填充。实现 apply_data_scope(query, ctx, permission, owner_column, dept_column) 按变体追加 filter。各 health handler 列表查询调用此函数。
验收: data_scope = Department 时查询自动追加部门过滤;未配置时默认 All 向后兼容。
Task 5: 微信 session_key 从 HashMap 迁移到 Redis
涉及文件: crates/erp-auth/src/service/wechat_service.rs, erp-server/src/app_state.rs
步骤: 替换 LazyLock<Mutex<HashMap>> 为 Redis SET wechat:session:{openid} {key} EX 300 / GET + DEL。Redis 连接池通过 AuthState 传入。添加 fallback:Redis 不可用时降级内存 HashMap 并 warn 日志。
验收: 小程序登录端到端通过;cargo test -p erp-auth 通过。
Task 6: 小程序 openid 加密存储
涉及文件: apps/miniprogram/src/utils/secure-storage.ts(新增), stores/auth.ts
步骤: 创建 AES 加密存储工具(setSecure/getSecure)。后端登录接口响应新增 storage_key 字段。auth.ts 中 openid 存储改用 setSecure('openid', openid)。
验收: Taro.getStorageSync('openid') 返回密文;小程序登录端到端通过。
Phase 3: 健康检查增强 + 审计日志(Week 2-3)
Task 7: /health/ready 增加 DB ping + Redis ping
涉及文件: crates/erp-server/src/handlers/health.rs
步骤: 添加 sqlx::query("SELECT 1") DB 检查 + redis PING 检查,使用 tokio::join! 并行。响应扩展 status(ok/degraded) + database + redis 字段。
验收: DB 不可用时返回 status: "degraded" + database: "unreachable"。
Task 8: audit_logs 表增加 prev_hash 字段实现哈希链
涉及文件: migration/src/m000074_audit_logs_hash_chain.rs(新增), erp-core/src/audit.rs
步骤: 迁移添加 prev_hash TEXT + record_hash TEXT 列。写入时查询最新 record_hash 作为 prev_hash,计算 SHA256(id + action + resource_type + resource_id + created_at + prev_hash) 作为 record_hash。添加完整性验证函数检测篡改。内存缓存最近 1000 条 record_hash 优化性能。
验收: 新审计日志含哈希链字段;修改记录后验证函数检测到链断裂;cargo test 通过。
执行原则
- 每 Task 完成后立即提交 — 不积压
- Phase 1 最高优先 — RLS 是医疗数据合规红线
- RLS 迁移必须可回退 — down 方法完整恢复
- 渐进式启用 data_scope — 未配置默认 All,不破坏现有行为