Files
im_manager/docs/admin-adfahi-research/服务端消息加密与内容审核改造文档.md
T

28 KiB
Raw Blame History

服务端内容审核与消息加密改造文档

文档状态:分阶段实施

更新日期:2026-09-16

适用项目:im_manager(Admin Server)、im_server(IM Server)

实施顺序

本项目拆成两个相互独立的实施部分,并统一保存在本文档中:

  1. 第一部分先完成内容审核,独立开发、灰度、验收和稳定观察;本阶段不修改消息存储格式。
  2. 第二部分在内容审核稳定后实施消息加密;该阶段单独立项、单独发布,并对消息表和历史数据进行分批迁移。

两部分禁止同批切换。第二部分只接管“内容审核通过后的消息存储”,不得反向修改第一部分已经验收的审核决策。

第一部分:
客户端 -> TLS -> IM Server -> 内容审核 -> 现有明文存储

第二部分:
客户端 -> TLS -> IM Server -> 内容审核 -> AES-256-GCM -> 密文存储

第二部分启动门禁

消息加密开发必须同时满足:

  1. 内容审核已全量上线并通过业务验收。
  2. 误拦截率、阿里云耗时、超时降级和熔断策略达到生产要求。
  3. 消息发送、编辑、资料修改和多 Pod 配置同步已经稳定。
  4. IM 数据库全量备份及恢复演练通过。
  5. 历史消息规模、迁移时间和数据库负载完成评估。
  6. 加密双读、密钥轮换和回滚方案已在测试环境演练通过。

第一部分:服务端内容审核(优先实施)

文档状态:第一阶段,优先开发

编制日期:2026-09-16

适用项目:im_manager(Admin Server)、im_server(IM Server)

本阶段不修改消息存储格式,不实施消息加密。

1. 目标

Admin Server 提供内容审核配置和商户策略管理,IM Server 在内容写入前根据策略执行:

  1. 本地敏感词补充检查。
  2. 阿里云 TextModerationPlus 文本审核。
  3. 按风险等级放行、拦截或仅审计。
  4. 记录审核结果、请求耗时和处置结果,不保存审核明文。

第一阶段只为 tenant.code=default 的上架默认商户开启阿里云审核。其他团队商户默认关闭,不产生阿里云调用和费用。

2. 不在本阶段实施的内容

  • 不改造 im_messages.content 和 im_group_messages.content。
  • 不建设 AES 加解密模块。
  • 不建设密钥管理表、密钥轮换和历史消息迁移。
  • 不做客户端加密或端到端加密。
  • 不改变现有 HTTPS/WSS 传输协议。
  • 图片、视频和文件本体继续走独立的文件审核链路。

3. 现有实现和差距

能力 现状 改造目标
阿里云文本审核 文档已人工验证,IM Server 无 Go 调用实现 实现 TextModerationPlus 客户端并接入业务链路
审核服务配置 im_external_config 已有 moderation 占位 补齐阿里云参数、真实连通性检查和发布
商户审核策略 im_security_policy.sensitive_word_policy 可保存 JSON 定义固定结构并提供后台配置
本地敏感词 Admin 已有 CRUD,IM Server 未消费 作为可选补充规则接入 IM 运行时
配置同步 已有 internal config、Kafka 配置事件和缓存失效 复用现有机制扩展审核策略
消息审核 消息直接保存和广播 在保存和广播前审核
昵称审核 无 覆盖用户昵称、群昵称和群名称的所有修改入口

4. 总体链路

客户端
  | HTTPS/WSS
  v
IM Server
  |-- 权限、宵禁、用户状态等现有校验
  |-- 根据tenant读取审核策略
  |-- 提取用户可见文本
  |-- 本地敏感词检查(可选)
  |-- 阿里云TextModerationPlus(可选)
  |-- 映射风险结果和处置动作
  |-- block:返回业务错误,不入库、不广播
  `-- allow/audit_only:按原逻辑入库和广播

5. 审核场景

场景 阿里云 Service 审核时机
用户昵称 nickname_detection_pro 更新用户资料前
用户签名 nickname_detection_pro 更新用户资料前
群昵称 nickname_detection_pro 更新群成员资料前
群名称 nickname_detection_pro 创建群或修改群资料前
单聊文本 chat_detection_pro 消息入库及广播前
群聊文本 chat_detection_pro 消息入库及广播前
编辑后消息 chat_detection_pro 新版本入库前
群公告 chat_detection_pro 发布或修改前

系统消息、WebRTC 信令、已签名的服务端消息和空文本不调用文本审核。

6. Admin Server 改造

6.1 阿里云服务配置

复用 im_external_config 的 service=moderation,只允许平台管理员维护:

配置 说明
provider 固定 aliyun_green
endpoint 默认 green-cip.cn-shanghai.aliyuncs.com
enabled 平台总开关
config_json.region_id 默认 cn-shanghai
config_json.nickname_service 默认 nickname_detection_pro
config_json.chat_service 默认 chat_detection_pro
config_json.timeout_ms 单次请求超时,建议 1000~2000 ms
config_json.failure_mode allow 或 block
secret_json.access_key_id 只写不回显
secret_json.access_key_secret 只写不回显

“检查配置”必须真实调用阿里云测试文本,返回成功与否、耗时、RequestID 和脱敏错误,不能只检查字段非空。

6.2 商户审核开关与策略

im_tenant_config 增加两个商户级权威开关,并在“商户管理”的新增/编辑页面维护:

字段 说明
aliyun_moderation_enabled 是否对该商户启用阿里云内容审核
local_moderation_enabled 是否对该商户启用本地敏感词审核

两个开关默认关闭;现有 default 商户迁移时保持开启。只要任一开关开启,该商户即进入内容审核流程。安全策略不再重复维护审核引擎开关,只维护场景、风险动作、第三方失败策略和拦截提示。

复用 im_security_policy.sensitive_word_policy,统一 JSON 结构:

{
  "scenes": {
    "user_nickname": true,
    "user_signature": true,
    "group_name": true,
    "group_nickname": true,
    "private_message": true,
    "group_message": true,
    "group_announcement": true
  },
  "risk_actions": {
    "high": "block",
    "medium": "block",
    "low": "audit_only",
    "none": "allow"
  },
  "failure_mode": "allow",
  "block_message": "内容不符合平台规范,请修改后重试"
}

规则:

  • 审核引擎是否启用以 im_tenant_config 的两个商户开关为准。
  • 每个商户可以独立开启阿里云审核、本地审核,或同时开启。
  • 商户管理员不得查看或修改阿里云 AK/SK。
  • 策略发布使用现有 im.config.event 通知 IM Server 按商户失效缓存。

6.3 本地敏感词

  • 现有 im_sensitive_words 和分组管理作为补充规则保留。
  • 只有商户配置 local_moderation_enabled=1 时才加载。
  • 敏感词和分组按商户隔离,平台管理员及当前商户管理员按权限维护。
  • 词库更新继续发布 sensitive_words.updated 事件。

6.4 内部接口

方法 路径 用途
GET /internal/im-config/moderation/provider 拉取阿里云审核服务配置
GET /internal/im-config/tenants/{code}/moderation-policy 拉取商户审核策略
GET /internal/im-config/tenants/{code}/sensitive-words 拉取商户有效敏感词

接口复用现有内部服务签名鉴权、时间窗、RequestID 和重放校验,禁止浏览器直接调用。

7. IM Server 改造

7.1 内部模块

server/moderation/
  provider.go              审核Provider接口
  policy.go                商户策略和缓存
  extractor.go             文本提取
  decision.go              风险结果映射
  local/words.go           本地敏感词匹配
  aliyun/text.go           TextModerationPlus实现

统一接口需支持:商户、场景、操作人、业务对象、文本和 TraceID,返回 allow/block/audit_only、风险等级、标签和可审计的脱敏结果。

7.2 消息发送接入

在 Topic.handlePubBroadcast 中,于 saveAndBroadcastMessage 之前:

  1. 排除 WebRTC/系统信令。
  2. 从 msg.Pub.Content 提取审核文本。
  3. 执行本地规则和阿里云审核。
  4. block 时通过 Tinode ctrl 返回业务错误,不调用保存和广播。
  5. allow/audit_only 时继续现有消息链路。

消息编辑必须重新审核编辑后内容,不复用原消息结果。

7.3 资料修改接入

用户昵称/签名、群名称/公告、群昵称的 HTTP 和 Tinode meta 修改通道必须接入同一 moderation service,防止通过另一个协议入口绕过审核。

7.4 文本提取

  • 普通文本:提取完整文本。
  • Drafty 富文本:提取 txt 及按钮、链接的用户可见标题。
  • 图片/文件/语音:提取文件名、说明和附带文本。
  • 禁止将任意 JSON 直接字符串化后送审,避免审核协议元数据。

7.5 阿里云结果映射

内部结果 动作
allow 继续保存和广播
block 不入库、不广播,返回统一业务错误
audit_only 允许业务继续,写审核日志
retryable_error 根据 failure_mode 放行或拦截

阿里云已测试样本表明,部分引流诈骗话术可能只出现在扩展判断中。实现时不能只查看单一 RiskLevel/Label,需保留脱敏原始响应以便调试,并使用真实样本建立回归集。

7.6 超时、降级和熔断

  • 阿里云请求不得在数据库事务中执行。
  • 必须设置独立超时,禁止无限等待。
  • 默认 failure_mode=allow,第三方异常时放行并告警,避免 IM 全局不可用。
  • 连续失败达阈值后短时熔断,熔断期间按 failure_mode 处理。
  • 本地敏感词检查不受阿里云熔断影响。

7.7 缓存和多 Pod

  • 阿里云全局配置、商户策略和敏感词分别缓存。
  • 策略缓存键至少包含 tenant_code + policy_version。
  • Kafka 配置事件和词库事件使所有 IM Pod 同时失效。
  • 事件消费必须幂等,丢失事件后仍能通过 TTL 重新加载。

8. 审核日志

在 IM 业务库增加 im_content_moderation_logs:

字段 说明
id 主键
request_id 内部审核请求 ID
scene 用户昵称、单聊、群聊等
topic / seqid 关联消息,拦截时 seqid 可为空
actor_id 内容提交人
provider local / aliyun_green
provider_request_id 阿里云 RequestID
risk_level 统一风险等级
risk_labels 命中标签 JSON
action allow / block / audit_only
content_hash 原文 SHA-256 摘要
latency_ms 审核耗时
error_code 脱敏错误码
created_at 创建时间

表中不保存原始明文、AK/SK 或完整第三方响应中的敏感信息。

9. 错误码

错误 说明 客户端处理
CONTENT_REJECTED 内容审核拒绝 保留未发送内容,提示用户修改
CONTENT_REVIEW_UNAVAILABLE 审核不可用且策略为拦截 提示稍后重试

不向客户端返回阿里云原始错误、命中词详情、AK/SK 或内部堆栈。

10. 监控

  • 按商户/场景统计请求量、放行数、拦截数、审计数和降级数。
  • 阿里云耗时 P50/P95/P99。
  • 超时率、错误率、熔断状态和熔断持续时间。
  • 按风险等级和标签统计命中数。
  • 配置版本、各 Pod 最后刷新时间和缓存命中率。
  • 监控标签不得放入原始消息文本。

11. 开发任务拆分

11.1 Admin Server

  1. 补齐 moderation 外部服务配置和真实连通性检查。
  2. 实现商户审核策略表单和发布。
  3. 限制默认商户以外的敏感词入口和后端权限。
  4. 增加阿里云配置、商户策略和词库 internal API。
  5. 发布并验证 Kafka 配置失效事件。

11.2 IM Server

  1. 实现 moderation 统一接口。
  2. 实现阿里云 TextModerationPlus 客户端。
  3. 实现商户策略、本地敏感词和阿里云配置缓存。
  4. 接入消息发送、消息编辑、用户资料和群资料入口。
  5. 增加审核日志表、错误映射、监控和告警。
  6. 补齐单元测试、集成测试和阿里云沙箱/测试账号回归。

12. 测试重点

  • 文本、Drafty、链接、表情、中英文混合和空内容提取。
  • 同一内容在单聊、群聊和编辑入口处置一致。
  • HTTP 和 Tinode meta 入口不能相互绕过。
  • default 商户开启,其他商户关闭。
  • 阿里云超时、500、限流、鉴权失败和熔断恢复。
  • 本地词库命中、阿里云命中、双重命中和无命中。
  • 多 Pod 收到配置发布后同步生效。
  • 拦截消息不入库、不广播,不占用有效消息序号。
  • 日志、Kafka、死信和监控中不出现消息明文和 AK/SK。

13. 验收标准

  1. default 商户的用户昵称、群昵称、群名称、单聊和群聊均真实调用对应阿里云 Service。
  2. 其他商户关闭审核时不调用阿里云。
  3. 拦截内容不进入消息表,不向任何订阅者广播。
  4. 本地敏感词与阿里云开关可独立启停。
  5. 阿里云超时和异常严格按 failure_mode 处理。
  6. 策略和词库发布后,所有 IM Pod 在规定时间内生效。
  7. 日志能追溯商户、场景、用户、RequestID、风险结果和处置动作,但不保存明文。
  8. 审核上线后独立稳定运行和观察,不依赖消息加密功能。

14. 发布顺序

  1. 先发布 Admin Server 配置表单、internal API 和配置事件。
  2. 发布 IM Server 审核模块,但保持所有商户策略关闭。
  3. 对阿里云连通性、超时降级和多 Pod 配置同步进行生产前验证。
  4. 仅对测试用户或小流量开启 default 商户审核。
  5. 观察误拦截率、阿里云耗时、错误率和消息总延迟。
  6. 完成全量开启和验收,进入独立稳定观察期。
  7. 审核功能稳定并通过验收后,才能启动消息加密阶段。

第二部分:服务端消息加密存储(审核稳定后实施)

文档状态:第二阶段,内容审核稳定后实施

编制日期:2026-09-16

适用项目:im_manager(Admin Server)、im_server(IM Server)

该改造会影响消息写入、历史同步、后台查询和数据迁移,必须单独发布和验收。

1. 实施前提

本阶段必须在《服务端内容审核改造文档》完成开发、全量上线和稳定观察后启动。

启动条件:

  1. 内容审核的误拦截率、耗时、超时和降级策略已验收。
  2. 审核与消息保存的边界已稳定,加密模块只接管“审核通过后的存储”。
  3. 已完成 IM 数据库全量备份和可恢复性演练。
  4. 已对 im_messages、im_message_revisions、im_group_messages 的数据量、存储量和迁移耗时完成评估。

2. 目标和非目标

2.1 目标

  • 审核通过的新消息在 IM Server 使用 AES-256-GCM 加密后落库。
  • 国内 IM 业务库不保存可读消息正文。
  • HotGo 库保存根密钥和加密后的商户数据密钥。
  • 客户端协议不变,IM Server 对历史消息解密后按原协议返回。
  • Admin Server 通过 IM internal-query 查看授权范围内的解密消息。
  • 支持密钥版本、轮换、退役和历史数据迁移。

2.2 非目标

  • 不做客户端 AES 加密。
  • 不做端到端加密(E2EE)。
  • 不改变 HTTPS/WSS 传输安全方案。
  • 不允许 Admin Server 直连 IM 数据库后自行解密。
  • 不在日志、Kafka、死信和监控标签中保存消息明文。

3. 安全边界和部署

区域 系统 保存数据
国外 HotGo / Admin Server 字典根密钥、商户消息密钥、加密开关和版本
国内 IM Server 用户、会话、消息密文和密钥版本

防护目标:任意一侧数据库单独泄露时,不能直接恢复消息明文。

已知边界:HotGo 数据库、IM 数据库和完整代码同时泄露不在本方案的主要防护范围内。

Admin Server 和 IM Server 跨公网通信必须使用 HTTPS、现有签名鉴权和重放防护,建议叠加 mTLS 或固定出口 IP 白名单。

4. 算法和存储格式

4.1 算法

  • 算法:AES-256-GCM。
  • DEK:32 字节强随机密钥。
  • Nonce:每次加密使用 crypto/rand 生成的独立 12 字节随机值。
  • GCM 标签:使用 Go 标准库默认 16 字节认证标签。
  • 明文:将原 content JSON 规范序列化为 UTF-8 字节。
  • 禁止固定 Nonce、AES-ECB 和未认证的 AES-CBC/CTR。

4.2 AAD

每条消息绑定:

tenant_id | topic | seqid | from | client_msg_id | key_version | content

content 是固定字段标识。AAD 使用固定字段顺序和长度前缀编码,不使用可形成拼接歧义的简单字符串。

4.3 密钥层级

HotGo字典根密钥(KEK)
    -> 加密每个商户、每个版本的DEK
        -> DEK加密该商户消息

5. HotGo 密钥管理

5.1 字典根密钥

根密钥使用 hg_sys_dict_data 保存,必须满足:

  1. 使用独立内部字典类型,不出现在通用字典管理页。
  2. 后端字典列表、选项、导出和缓存接口必须服务端拒绝该类型,不得仅隐藏菜单。
  3. 根密钥是 32 字节强随机值的 Base64 编码,不使用口令、UUID 或可读文本。
  4. 根密钥只由 Admin Server 专用密钥服务读取。
  5. 根密钥不回显、不写日志、不进入 Kafka,不通过通用配置接口返回。

5.2 商户密钥表

新增 im_message_encryption_keys:

字段 类型 说明
id bigint 主键
tenant_id bigint 商户 ID
key_version int 密钥版本,从 1 递增
algorithm varchar(32) 固定 AES-256-GCM
encrypted_dek blob KEK 加密后的 DEK
dek_nonce varbinary(12) 加密 DEK 时的随机 Nonce
status varchar(16) pending / active / retired / disabled
created_at datetime 创建时间
activated_at datetime 启用时间
retired_at datetime 退役时间
created_by bigint 操作人
remark varchar(255) 备注

约束:

  • 唯一键:tenant_id + key_version。
  • 每个商户同时只能有一把 active 密钥。
  • retired 密钥可解密历史数据,不得加密新消息。
  • disabled 只用于安全事故应急,禁用前必须完成历史转密钥。

5.3 后台管理页

只允许最高权限平台管理员:

  • 查看商户加密状态、当前密钥版本和最后轮换时间。
  • 初始化商户密钥。
  • 启用或暂停消息加密。
  • 轮换密钥。
  • 查看历史迁移进度。

页面不展示根密钥、DEK、encrypted_dek 和任何可恢复密钥的内容。

6. IM 数据库改造

6.1 im_messages 和 im_message_revisions

新增:

字段 类型 说明
content_ciphertext longblob 密文,包含 GCM 认证标签
content_nonce varbinary(12) 每条消息独立随机 Nonce
key_version int 商户 DEK 版本
encryption_alg varchar(32) AES-256-GCM
encrypted tinyint 0 明文兼容,1 密文

迁移期规则:

  • 新加密数据:content=NULL、encrypted=1。
  • 旧数据:content IS NOT NULL、encrypted=0。
  • 解密后必须恢复为原 any JSON 结构,不修改客户端协议。
  • head 第一阶段不加密,但不得放置消息正文、联系方式等敏感明文。

6.2 im_group_messages

im_group_messages 是群消息派生查询表,不得另存一份明文。优先方案:

  • 仅保存索引、状态、审核和统计字段。
  • 通过 topic + seqid 关联 im_messages 获取密文并解密。
  • 迁移后清空或删除派生表中的明文 content。

如果必须保留独立内容列,则必须同步改为密文字段,禁止主表已加密而派生表仍保留明文。

6.3 Schema 一致性

  • MySQL 普通库和共享库/多商户 adapter 同时改造。
  • 新建库 schema、启动 ensure 迁移和手工 SQL 脚本保持一致。
  • types.Message 和 types.MessageRevision 增加密文元数据。
  • Save、GetOne、GetAll、编辑、撤回、删除、修订记录和后台查询全部覆盖。

7. IM Server 加解密模块

server/messagecrypto/
  cipher.go                AES-256-GCM加解密
  aad.go                   AAD稳定编码
  key_provider.go          密钥获取接口
  key_cache.go             tenant/version内存缓存
  serializer.go            content规范序列化

不将 AES 逻辑或 HotGo 调用直接写入 MySQL adapter。adapter 负责存储密文模型,加解密由业务存储服务边界统一执行。

7.1 新消息

完成现有校验
  -> 完成内容审核
  -> 分配seqid并组装AAD
  -> 序列化content
  -> 使用active DEK加密
  -> 事务写入密文和元数据
  -> 向在线订阅者广播内存中的原明文

审核必须发生在加密之前。广播使用请求内存中的明文,不执行“加密后立即解密”。

7.2 历史消息和离线同步

读取数据库记录
  -> encrypted=0:读原content
  -> encrypted=1:按tenant + key_version获取DEK
  -> 重建AAD
  -> GCM验签并解密
  -> JSON反序列化
  -> 按原Tinode协议返回

GCM 验签失败必须当作数据篡改或密钥错误,不得忽略错误或返回部分内容。

8. 密钥下发、缓存和轮换

8.1 内部接口

方法 路径 用途
GET /internal/im-config/tenants/{code}/message-keys/active 获取当前 DEK
GET /internal/im-config/tenants/{code}/message-keys/{version} 获取指定版本 DEK
POST /internal/im-config/tenants/{code}/message-keys/ack IM Server 确认新密钥可用

这些接口只允许 IM Server 服务身份调用,不得暴露给浏览器或通用 Admin API。

8.2 缓存

  • IM Server 不得每条消息都跨境请求 HotGo。
  • 按 tenant_id + key_version 在内存缓存已解密 DEK。
  • 新消息使用 active 密钥,历史消息按存储版本取密钥。
  • 每个 Pod 持有本地缓存,Kafka 事件使全部 Pod 失效。
  • HotGo 短时不可用时,已有内存密钥的 Pod 可继续工作。
  • 无可用密钥时禁止写入新消息,不得降级为明文落库。

8.3 轮换

  1. Admin Server 生成新 DEK,使用 KEK 加密后以 pending 入库。
  2. 通知 IM Server 拉取、解包并完成加解密自检。
  3. IM Server 回传 ACK 后,在事务中将旧版本设为 retired,新版本设为 active。
  4. 新消息立即使用新版本,旧消息仍按旧版本解密。
  5. 历史转密钥作为独立低速任务,不阻塞即时通信。

9. Admin 后台查看明文

  • Admin Server 调用 IM internal admin-query,不直连 IM 库解密。
  • IM Server 完成商户数据权限校验后,只解密当前分页数据。
  • 记录管理员 ID、商户、IP、查询条件、返回数量、时间和 TraceID。
  • 列表解密、详情解密和导出解密使用不同审计动作。
  • 默认不提供明文批量导出。后续开放时必须增加独立权限和审批。

10. 关键词查询

加密后 MySQL 无法对密文执行 LIKE '%keyword%'。第一阶段采用:

  1. 查询必须限定商户和时间范围,并尽量限定用户或群号。
  2. IM Server 使用明文元数据缩小候选集。
  3. 对有上限的候选集批量解密,在内存中执行关键词匹配。
  4. 超过时间范围或候选数限制时返回明确提示。

第一版不建设明文分词索引。如后续必须支持全库高性能检索,再单独评审 HMAC 盲索引。

11. 历史数据迁移

11.1 发布顺序

  1. 发布 HotGo 密钥结构、密钥服务和管理页。
  2. 为默认商户初始化第 1 版 DEK,但不开启加密写入。
  3. 发布 IM Server 双读版本,验证密钥获取、加解密和所有读写链路。
  4. 小流量开启新消息加密,旧数据仍可明文读取。
  5. 全量开启新消息加密后,启动历史数据分批迁移。
  6. 数量对账、抽样解密和 GCM 验签通过后,逐批清空明文 content。
  7. 观察期结束后,再决定是否删除原明文字段。

11.2 迁移任务

  • 按主键或 topic + seqid 分批,不长时锁表。
  • 每批独立事务,记录游标、成功数、失败数和最后错误。
  • 重复执行幂等,encrypted=1 直接跳过。
  • 先写密文并验证可解密,再将明文置空。
  • 失败记录保留原数据并进入重试列表,不得丢失数据。
  • 限制迁移速率,监控数据库负载和即时消息延迟。

12. 错误处理

错误 说明 客户端/后台处理
MESSAGE_ENCRYPTION_UNAVAILABLE 未取得当前加密密钥 暂停新消息写入,不明文降级
MESSAGE_DECRYPT_FAILED 验签或解密失败 显示“消息暂时无法加载”,不返回乱码
MESSAGE_KEY_VERSION_NOT_FOUND 历史密钥版本不存在 报警并阻止该记录解密

客户端不参与加解密,不返回密钥版本、Nonce、密文、解密堆栈或内部错误。

13. 监控和审计

  • 加密/解密成功数、失败数和耗时。
  • 各商户当前密钥版本。
  • 密钥获取成功率、缓存命中率和最后刷新时间。
  • 明文历史记录剩余数、迁移速率和迁移失败数。
  • GCM 验签失败产生高优先级告警。
  • 后台明文查看记录管理员、商户、IP、查询范围、返回数量和 TraceID。
  • 监控标签和日志不得保存消息正文、密文、Nonce、DEK 或 KEK。

14. 开发任务拆分

14.1 Admin Server

  1. 根密钥字典隔离和通用接口防护。
  2. im_message_encryption_keys 及业务逻辑。
  3. 消息加密管理页面和最高权限控制。
  4. 密钥下发、ACK 和 Kafka 失效事件。
  5. 密钥初始化、轮换、退役和审计。

14.2 IM Server

  1. AES-256-GCM、AAD 和规范序列化模块。
  2. 密钥获取、多版本缓存和多 Pod 失效。
  3. 新消息加密写入和双读解密。
  4. 单聊、群聊、修订、离线同步和后台查询覆盖。
  5. 历史数据迁移、进度统计和失败重试。
  6. 加解密监控、验签告警和后台解密审计。

15. 测试重点

  • 相同明文连续加密两次,Nonce 和密文不同。
  • 修改密文、Nonce、AAD 或 key_version 任意一项后解密失败。
  • JSON 字符串、数组、对象、Drafty、空值和超长内容往返一致。
  • 在线发送、离线同步、多端漫游、编辑、回复、撤回和删除行为不变。
  • 新旧消息混合分页和历史同步。
  • 新旧密钥版本并存和多 Pod 切换。
  • HotGo 暂时不可用、IM Pod 重启且无缓存、密钥版本缺失。
  • 历史迁移中断、重试、幂等和数量对账。
  • 旧密钥退役后可读旧消息,新消息只使用新密钥。
  • 日志、Kafka、错误响应和监控中无明文和密钥。

16. 验收标准

  1. 开启商户的新消息在数据库中不存在可读正文。
  2. 相同内容的 Nonce 和密文不同。
  3. 篡改密文、Nonce 或 AAD 后必须验签失败。
  4. 所有在线和历史消息功能保持原行为。
  5. Admin 有权限账号可查看当页解密明文,无权限账号被拒绝。
  6. 后台明文查看形成完整审计记录。
  7. 旧密钥退役后仍可解密旧消息,新消息只使用新版本。
  8. HotGo 短时断开时,有内存密钥的 Pod 可工作;无密钥时不得明文降级。
  9. 历史数据迁移完成后,im_messages、im_message_revisions、im_group_messages 无明文副本。
  10. 纯 AES 加解密不得成为消息链路主要瓶颈,密钥缓存命中时不产生跨境请求。

17. 上线和回滚

  1. 第一次只为测试商户或小流量用户开启新消息加密。
  2. 加密开关必须按商户生效,不做全局一次性切换。
  3. 观察写入失败、解密失败、同步延迟、数据库负载和密钥缓存。
  4. 未开始历史迁移前,可停止新加密写入,但已加密消息仍必须保持可解密读取。
  5. 禁止通过删除旧密钥实现回滚。
  6. 历史迁移开始后,回滚必须保留双读能力,不允许只回滚数据库结构或只回滚代码。