Files
railwaysystem/docs/development/dms-r3/08_security_and_audit.md
T

122 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 08 敏感数据合规、凭据安全与门禁审计体系 (Security and Audit)
> **安全最高红线**:智能门锁直接关联学员人身财产安全与公寓物理边界防线。
> **设计原则**:**最小知悉原则、凭据零明文长期留存、全链路日志脱敏、关键动作两票制双人审计轨迹**。
---
## 1. 门禁凭据类型全面比选与首期试点推荐
### 1.1 四类门锁开门凭据深度对比矩阵
| 评估维度 | 在线临时密码 (Online Password) | 在线实体学员卡 (Online Card) | 离线算法密码 (Offline Password) | 远程网络开锁 (Remote Unlock) |
| :--- | :--- | :--- | :--- | :--- |
| **实施复杂度** | **低**(纯软件 API 交互) | **中**(需发卡设备与现场制卡) | 高(需手工向学员通知长串数字) | 低(API 触发) |
| **硬件与耗材依赖** | **零硬件依赖**(手机/短信/凭条) | **依赖物理卡片**及 USB 读写卡器 | 零硬件依赖 | 依赖集中网关长连接 |
| **是否需要学员手机号** | 是(DMS 接口要求 `phoneNumber`) | 否(仅需卡号十六进制) | 否 | 否 |
| **支持精准有效期** | **支持**(毫秒级 `validStart/Until`) | **支持**(毫秒级 `validStart/Until`) | 仅支持固定颗粒度(如天/时) | 一次性即时脉冲触发 |
| **远程主动撤权能力** | **强**(可随时调用 delete 接口撤销) | **强**(可随时调用 delete 接口撤销) | **极弱**(撤权需在锁上输入清空码) | 不适用 |
| **门锁开门审计追溯** | **强**(记录包含持有人与密码标识) | **强**(记录包含卡号与持有人) | 弱(无法区分具体使用者身份) | 强(记录远程操作日志) |
| **离线网络容灾能力** | 弱(下发与撤销时门锁必须在线) | 弱(下发与撤销时门锁必须在线) | **极强**(门锁离线依然可验证算法) | 无(断网完全无法执行) |
| **现场运维与损耗成本** | **零损耗成本** | 存在卡片丢失、补卡、消磁运维成本 | 需人工现场解决锁具失步问题 | 零物理成本,但安全风险极高 |
### 1.2 首期接入推荐结论 (`RECOMMENDED_PILOT_CREDENTIAL`)
- **【首推试点方案】在线限时密码 (Online Password)**:
- **采纳理由**:零硬件附加成本;无需采购现场读写发卡器;完全通过软件 API 自动派发与注销;天然支持培训期间精准起止时间;开门流水完整可溯。
- **【备选方案】在线实体卡 (Online Card)**:
- 若沈阳房建公寓段已统一配发带有芯片的学员出入证/铁路一卡通,可作为二阶段或并行备选,需配备发卡器及编码转换组件。
- **【其他方案评估】离线密码与二维码钥匙**:
- 离线密码缺乏主动撤销能力,一期明确排除 (`OUT_OF_SCOPE`)。
- 二维码/手机钥匙虽在官方文档中存在明确端点 (`POST /rest/doorlock/qrcode/create` 与 `POST /rest/qrcode/delete`),分类为 `DOCUMENTED`,但返回体存在字段污染且无二维码具体载荷内容,其实际钥匙方案可行性归为 `NEEDS_BUSINESS_CONFIRMATION`,一期不作为首选试点方案。
---
## 2. 在线实体卡卡号编码规约与 `CardNumberCodec` 设计
官方文档对在线卡片(`POST /rest/doorlock/online-card/create`)的卡号规定了非常特殊的**小端字节序十六进制转换规则**:
```mermaid
flowchart TD
Step1["读卡器读取 10 进制原始卡号"] -->|"例: 3161082660"| Step2["转成标准 16 进制大端串"]
Step2 -->|"例: BC 6A 4B 24"| Step3["不足 8 位时高位补零"]
Step3 -->|"例: 09 99 ED 24"| Step4["低位在前的字节序逆序转换 (Little-Endian)"]
Step4 -->|"例: 24 4B 6A BC 或 24 ED 99 09"| Step5["送入 DMS API 的 cardNumber 参数"]
```
### 2.1 架构封装纪律
- **严禁**在 Controller 或前端编写卡号翻转逻辑。
- 必须在 `com.ruoyi.train.integration.dms.util` 包中独立封装 `CardNumberCodec` 工具类,包含严格的正则表达式校验与单测用例覆盖(大端转小端、奇数位补零、10进制溢出校验)。
---
## 3. 敏感数据存储与展示安全策略
### 3.1 开门临时密码保护规范
- **密码长度规范与零明文落库**:
- DMS 原生支持 4~8 位数字密码,系统 Pilot 建议生成 6 位限时密码。如果必须在数据库留存备查,必须采用 **AES-256-GCM** 对称加密存储(密钥由配置中心安全注入),严禁裸明文入库。
- **学员端展示规范**:
- 学员在移动端或大屏查看门禁密码时,默认采用脱敏掩码(如 `****34`)。
- 支持【点击查看】一次性展示:需经过二次验证,明文仅在内存中暂存并在 30 秒后自动模糊遮挡。
### 3.2 日志全局脱敏规则 (`Log Redaction`)
在系统的 Logback、HttpTrace 日志切面及审计日志中,必须对以下字段强制执行脱敏:
- `password` / `fuzzyPassword`:统一脱敏为 `******`。
- `appsecret` / `token`:绝对禁止打印,统一替换为 `[REDACTED_SECRET]`。
- `phoneNumber`:国内手机号统一脱敏为 `189****0952`。
- `cardNumber`:卡号中间 4 位脱敏。
---
## 4. 远程开锁高风险主动控制规约 (`Remote Unlock Risk`)
`POST /rest/doorlock/remote-unlock` 属于**最高风险的主动物理控制操作**。一旦滥用或被恶意调用,将直接打开学员宿舍房门。
### 4.1 控制红线要求
1. **一期范围判定**:
- **默认不在一期学员与常规宿管界面开放**,标记为 `P2_FUTURE / 受控应急`。
2. **安全防护机制(若未来业务强制启用)**:
- **专属高危权限标识**:定义为 `train:access:remoteUnlock`。
- **角色范围限定**:仅限系统超级管理员或经段保卫科书面授权的当值值班长使用。严禁授予任课教师 (`Teacher`) 或学员 (`Student`)。
- **二次身份确认**:界面弹出高危告警弹窗,强制要求操作员输入当前登录密码或动态验证码。
- **必填操作事由**:操作前必须下拉选择或录入事由(如“消防巡检”、“医疗急救”、“钥匙失效人工放行”),且字数不得少于 10 字。
- **全要素审计日志**:自动抓取并记录:操作人账号、操作人真实姓名、终端客户端 IP、User-Agent、目标楼栋房间、门锁硬件 SN、操作原因、DMS 返回的 `operationId` 与执行结果。
---
## 5. 门禁全生命周期审计轨迹设计 (`Audit Trail`)
系统必须在 `train_door_access_audit`(或 RuoYi 标准 `sys_oper_log` 基础上扩展)中建立不可篡改的门禁操作流水。
### 5.1 审计要素清单
每条审计记录必须完整包含以下 14 个不可变字段:
1. `audit_id`: 全局唯一审计流水号
2. `business_type`: 业务类型(`GRANT` 授权下发, `REVOKE` 授权注销, `RETRY` 失败重试, `REMOTE_UNLOCK` 远程开锁)
3. `accommodation_id`: 关联的本地住宿事实 ID
4. `enrollment_id` & `student_name`: 目标学员信息
5. `dorm_building`, `dorm_floor`, `dorm_room`: 目标宿舍物理位置
6. `dms_lock_id` & `lock_hardware_id`: DMS 门锁标识
7. `credential_type`: 凭据类别(`ONLINE_PASSWORD`, `ONLINE_CARD`)
8. `dms_key_id`: DMS 返回的钥匙全局凭据 ID
9. `dms_operation_id`: DMS 异步执行任务流水号
10. `operation_result`: 最终结果(`SUCCESS`, `FAILED`, `TIMEOUT`)
11. `error_message`: 详细失败信息(若有)
12. `operator_id` & `operator_name`: 操作发起人(系统自动触发时标为 `SYSTEM_AUTO`)
13. `client_ip`: 发起操作的客户端 IP
14. `operate_time`: 操作发生时间戳
### 5.2 响应数据持久化策略 (`Response Sanitization Policy`)
- **禁止项**:绝不无脑把外部 DMS 返回的几千字节原始 JSON 直接整体塞入数据库的文本列中。
- **要求**:仅抽取核心对账字段进行结构化持久化;若出于排障目的保留原始请求与响应报文快照,**必须先经过脱敏过滤器(剔除明文密码与密钥)**后再写入 `sanitized_response_payload`。
---
## 6. Wave 2 Implementation Result (Wave 2 implemented against Mock DMS only)
- **阶段状态**:Wave 2 implemented against Mock DMS only。
- **安全红线核验结论**:
- `PLAINTEXT_PASSWORD_PERSISTENCE_COUNT = 0`:反射测试断言 `TrainExternalOperation` 与 `TrainDormAuth` 绝无密码明文属性,实体中仅保留 `external_key_id`。
- `SECRET_LOGGING_COUNT = 0`:`DmsHttpClient` 与 `DmsOperationPoller` 全局应用 `sanitize` 过滤器,请求头与请求体中的 `password`、`token`、`appsecret` 均被强制替换为 `[PROTECTED]`。
- `LIVE_DMS_REQUEST_COUNT = 0`:开发与自动化测试期间严格仅使用内嵌 `MockDmsServer`,真实 DMS 地址仅保留于默认配置占位符,未发起任何公网请求。
- **迁移标记**:
- `008_RUNTIME_FIRST_RUN = NOT_EXECUTED`,`008_RUNTIME_SECOND_RUN = NOT_EXECUTED`。
- `RUNTIME_MIGRATION_GATE_BEFORE_PRODUCTION = REQUIRED`。