28 KiB
服务端内容审核与消息加密改造文档
文档状态:分阶段实施
更新日期:2026-09-16
适用项目:
im_manager(Admin Server)、im_server(IM Server)
实施顺序
本项目拆成两个相互独立的实施部分,并统一保存在本文档中:
- 第一部分先完成内容审核,独立开发、灰度、验收和稳定观察;本阶段不修改消息存储格式。
- 第二部分在内容审核稳定后实施消息加密;该阶段单独立项、单独发布,并对消息表和历史数据进行分批迁移。
两部分禁止同批切换。第二部分只接管“内容审核通过后的消息存储”,不得反向修改第一部分已经验收的审核决策。
第一部分:
客户端 -> TLS -> IM Server -> 内容审核 -> 现有明文存储
第二部分:
客户端 -> TLS -> IM Server -> 内容审核 -> AES-256-GCM -> 密文存储
第二部分启动门禁
消息加密开发必须同时满足:
- 内容审核已全量上线并通过业务验收。
- 误拦截率、阿里云耗时、超时降级和熔断策略达到生产要求。
- 消息发送、编辑、资料修改和多 Pod 配置同步已经稳定。
- IM 数据库全量备份及恢复演练通过。
- 历史消息规模、迁移时间和数据库负载完成评估。
- 加密双读、密钥轮换和回滚方案已在测试环境演练通过。
第一部分:服务端内容审核(优先实施)
文档状态:第一阶段,优先开发
编制日期:2026-09-16
适用项目:
im_manager(Admin Server)、im_server(IM Server)本阶段不修改消息存储格式,不实施消息加密。
1. 目标
Admin Server 提供内容审核配置和商户策略管理,IM Server 在内容写入前根据策略执行:
- 本地敏感词补充检查。
- 阿里云
TextModerationPlus文本审核。 - 按风险等级放行、拦截或仅审计。
- 记录审核结果、请求耗时和处置结果,不保存审核明文。
第一阶段只为 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 之前:
- 排除 WebRTC/系统信令。
- 从
msg.Pub.Content提取审核文本。 - 执行本地规则和阿里云审核。
block时通过 Tinode ctrl 返回业务错误,不调用保存和广播。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
- 补齐
moderation外部服务配置和真实连通性检查。 - 实现商户审核策略表单和发布。
- 限制默认商户以外的敏感词入口和后端权限。
- 增加阿里云配置、商户策略和词库 internal API。
- 发布并验证 Kafka 配置失效事件。
11.2 IM Server
- 实现 moderation 统一接口。
- 实现阿里云
TextModerationPlus客户端。 - 实现商户策略、本地敏感词和阿里云配置缓存。
- 接入消息发送、消息编辑、用户资料和群资料入口。
- 增加审核日志表、错误映射、监控和告警。
- 补齐单元测试、集成测试和阿里云沙箱/测试账号回归。
12. 测试重点
- 文本、Drafty、链接、表情、中英文混合和空内容提取。
- 同一内容在单聊、群聊和编辑入口处置一致。
- HTTP 和 Tinode meta 入口不能相互绕过。
default商户开启,其他商户关闭。- 阿里云超时、500、限流、鉴权失败和熔断恢复。
- 本地词库命中、阿里云命中、双重命中和无命中。
- 多 Pod 收到配置发布后同步生效。
- 拦截消息不入库、不广播,不占用有效消息序号。
- 日志、Kafka、死信和监控中不出现消息明文和 AK/SK。
13. 验收标准
default商户的用户昵称、群昵称、群名称、单聊和群聊均真实调用对应阿里云 Service。- 其他商户关闭审核时不调用阿里云。
- 拦截内容不进入消息表,不向任何订阅者广播。
- 本地敏感词与阿里云开关可独立启停。
- 阿里云超时和异常严格按
failure_mode处理。 - 策略和词库发布后,所有 IM Pod 在规定时间内生效。
- 日志能追溯商户、场景、用户、RequestID、风险结果和处置动作,但不保存明文。
- 审核上线后独立稳定运行和观察,不依赖消息加密功能。
14. 发布顺序
- 先发布 Admin Server 配置表单、internal API 和配置事件。
- 发布 IM Server 审核模块,但保持所有商户策略关闭。
- 对阿里云连通性、超时降级和多 Pod 配置同步进行生产前验证。
- 仅对测试用户或小流量开启
default商户审核。 - 观察误拦截率、阿里云耗时、错误率和消息总延迟。
- 完成全量开启和验收,进入独立稳定观察期。
- 审核功能稳定并通过验收后,才能启动消息加密阶段。
第二部分:服务端消息加密存储(审核稳定后实施)
文档状态:第二阶段,内容审核稳定后实施
编制日期:2026-09-16
适用项目:
im_manager(Admin Server)、im_server(IM Server)该改造会影响消息写入、历史同步、后台查询和数据迁移,必须单独发布和验收。
1. 实施前提
本阶段必须在《服务端内容审核改造文档》完成开发、全量上线和稳定观察后启动。
启动条件:
- 内容审核的误拦截率、耗时、超时和降级策略已验收。
- 审核与消息保存的边界已稳定,加密模块只接管“审核通过后的存储”。
- 已完成 IM 数据库全量备份和可恢复性演练。
- 已对
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 字节认证标签。
- 明文:将原
contentJSON 规范序列化为 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 保存,必须满足:
- 使用独立内部字典类型,不出现在通用字典管理页。
- 后端字典列表、选项、导出和缓存接口必须服务端拒绝该类型,不得仅隐藏菜单。
- 根密钥是 32 字节强随机值的 Base64 编码,不使用口令、UUID 或可读文本。
- 根密钥只由 Admin Server 专用密钥服务读取。
- 根密钥不回显、不写日志、不进入 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。 - 解密后必须恢复为原
anyJSON 结构,不修改客户端协议。 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 轮换
- Admin Server 生成新 DEK,使用 KEK 加密后以
pending入库。 - 通知 IM Server 拉取、解包并完成加解密自检。
- IM Server 回传 ACK 后,在事务中将旧版本设为
retired,新版本设为active。 - 新消息立即使用新版本,旧消息仍按旧版本解密。
- 历史转密钥作为独立低速任务,不阻塞即时通信。
9. Admin 后台查看明文
- Admin Server 调用 IM internal admin-query,不直连 IM 库解密。
- IM Server 完成商户数据权限校验后,只解密当前分页数据。
- 记录管理员 ID、商户、IP、查询条件、返回数量、时间和 TraceID。
- 列表解密、详情解密和导出解密使用不同审计动作。
- 默认不提供明文批量导出。后续开放时必须增加独立权限和审批。
10. 关键词查询
加密后 MySQL 无法对密文执行 LIKE '%keyword%'。第一阶段采用:
- 查询必须限定商户和时间范围,并尽量限定用户或群号。
- IM Server 使用明文元数据缩小候选集。
- 对有上限的候选集批量解密,在内存中执行关键词匹配。
- 超过时间范围或候选数限制时返回明确提示。
第一版不建设明文分词索引。如后续必须支持全库高性能检索,再单独评审 HMAC 盲索引。
11. 历史数据迁移
11.1 发布顺序
- 发布 HotGo 密钥结构、密钥服务和管理页。
- 为默认商户初始化第 1 版 DEK,但不开启加密写入。
- 发布 IM Server 双读版本,验证密钥获取、加解密和所有读写链路。
- 小流量开启新消息加密,旧数据仍可明文读取。
- 全量开启新消息加密后,启动历史数据分批迁移。
- 数量对账、抽样解密和 GCM 验签通过后,逐批清空明文
content。 - 观察期结束后,再决定是否删除原明文字段。
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
- 根密钥字典隔离和通用接口防护。
im_message_encryption_keys及业务逻辑。- 消息加密管理页面和最高权限控制。
- 密钥下发、ACK 和 Kafka 失效事件。
- 密钥初始化、轮换、退役和审计。
14.2 IM Server
- AES-256-GCM、AAD 和规范序列化模块。
- 密钥获取、多版本缓存和多 Pod 失效。
- 新消息加密写入和双读解密。
- 单聊、群聊、修订、离线同步和后台查询覆盖。
- 历史数据迁移、进度统计和失败重试。
- 加解密监控、验签告警和后台解密审计。
15. 测试重点
- 相同明文连续加密两次,Nonce 和密文不同。
- 修改密文、Nonce、AAD 或
key_version任意一项后解密失败。 - JSON 字符串、数组、对象、Drafty、空值和超长内容往返一致。
- 在线发送、离线同步、多端漫游、编辑、回复、撤回和删除行为不变。
- 新旧消息混合分页和历史同步。
- 新旧密钥版本并存和多 Pod 切换。
- HotGo 暂时不可用、IM Pod 重启且无缓存、密钥版本缺失。
- 历史迁移中断、重试、幂等和数量对账。
- 旧密钥退役后可读旧消息,新消息只使用新密钥。
- 日志、Kafka、错误响应和监控中无明文和密钥。
16. 验收标准
- 开启商户的新消息在数据库中不存在可读正文。
- 相同内容的 Nonce 和密文不同。
- 篡改密文、Nonce 或 AAD 后必须验签失败。
- 所有在线和历史消息功能保持原行为。
- Admin 有权限账号可查看当页解密明文,无权限账号被拒绝。
- 后台明文查看形成完整审计记录。
- 旧密钥退役后仍可解密旧消息,新消息只使用新版本。
- HotGo 短时断开时,有内存密钥的 Pod 可工作;无密钥时不得明文降级。
- 历史数据迁移完成后,
im_messages、im_message_revisions、im_group_messages无明文副本。 - 纯 AES 加解密不得成为消息链路主要瓶颈,密钥缓存命中时不产生跨境请求。
17. 上线和回滚
- 第一次只为测试商户或小流量用户开启新消息加密。
- 加密开关必须按商户生效,不做全局一次性切换。
- 观察写入失败、解密失败、同步延迟、数据库负载和密钥缓存。
- 未开始历史迁移前,可停止新加密写入,但已加密消息仍必须保持可解密读取。
- 禁止通过删除旧密钥实现回滚。
- 历史迁移开始后,回滚必须保留双读能力,不允许只回滚数据库结构或只回滚代码。