Files
hms/docs/superpowers/plans/2026-04-26-security-defense-in-depth.md
iven b410fa9f78
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
docs: 5 份实施计划 — 性能/安全/事件/前端/可观测性
对应 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/告警
2026-04-27 08:00:50 +08:00

4.7 KiB
Raw Blame History

安全纵深防御实施计划

设计规格: 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 RedisWeek 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 传入。添加 fallbackRedis 不可用时降级内存 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 通过。


执行原则

  1. 每 Task 完成后立即提交 — 不积压
  2. Phase 1 最高优先 — RLS 是医疗数据合规红线
  3. RLS 迁移必须可回退 — down 方法完整恢复
  4. 渐进式启用 data_scope — 未配置默认 All不破坏现有行为