审计链
每一个与安全相关的操作都被加密链接。设计上即可发现篡改。
HMAC-SHA256 行级链接、只追加的数据库触发器,以及每个租户单调递增的序列号。三个独立的篡改信号在表结构里,而非事后加上去。
为什么这不是一个“功能”
我们的审计日志在行级别被加密链接
即使攻击者拥有数据库的完整写权限,也无法在不留下证据的情况下伪造或删除一行。我们使用与区块链账本相同的哈希链原语,在行级别应用,以 HMAC 密钥代替共识协议。
7 年。2,555 天。每个用户的每个与安全相关的操作,都被采集且可被验证。
工作原理
三个字段让每一行成为证据
audit_logs 表中的每一行都带有三个保护字段。表本身平平无奇;纪律在于每次插入时被计算并签名的内容。
sequence
按租户单调递增的整数。序列号出现缺口就意味着某行被删除了,验证器会标出第一个缺失的编号。
prev_hash
指向上一行的 row_hash。链断了就意味着有行被重排或外部插入。
row_hash
对 {tenant_id, sequence, prev_hash, actor_id, action, resource_type, resource_id, payload, created_at} 的规范化序列做 HMAC-SHA256,使用 AUDIT_HMAC_KEY 签名;该密钥由应用程序持有,不在数据库里。
三个篡改信号
我们能检测什么,以及怎么检测
| 攻击 | 我们检测到的现象 | 检测方式 |
|---|---|---|
| 行篡改 | 已存储的 row_hash 不再等于重新计算的 HMAC。 | 验证器沿链行走,从每行的规范化负载重新计算。 |
| 行删除 | 出现序列号缺口,且幸存行的 prev_hash 不再对应其真实前驱。 | 序列检查加链式检查;任一项都足以发现问题。 |
| 行插入或重排 | 被插入的行无法引用一个真实的 prev_hash,链在被插入行或其后的行处断裂。 | 链式行走在第一个无效 hash 处发现不连续。 |
纵深防御
三层分离,是刻意为之
只追加的强制
PostgreSQL 触发器拦截一切对 audit_logs 的 DELETE。只有在已设 SET LOCAL app.audit_retention_cleanup = on 的事务里才允许删除,而这个标记只由保留期清理流程持有。SET LOCAL 的作用域局限于事务,因此提交或回滚时会自动清除,无法泄漏到后续语句。
密钥分离
HMAC 密钥在应用配置里,而不在数据库里。即便数据库被以只读方式攻陷,也无法伪造有效行。即便写入级别的数据库被攻陷,下一次验证链时也能发现:验证器使用 HMAC 密钥重新计算每一行,并指出第一行不再匹配的记录。
验证器独立
验证器使用应用持有的 HMAC 密钥对租户的链做端到端检查,并核对检查是否抵达租户上保存的链尾,因此删除最新的几行也会被发现。目前按需运行;按日的定时运行将随正式上线一同推出。
记录了哪些事件
每一个触碰运营记录的事件
- 01所有认证:登录、退出、MFA 注册、修改密码、找回密码
- 02所有授权:角色变更、权限授予、API 密钥的创建与吊销
- 03所有敏感管理操作:租户设置、保留期变更、RLS 上下文覆盖
- 04所有工单签字,附签字载荷的 SHA-256
- 05所有订阅与计费事件:webhook 接收、套餐变更、客户门户会话
- 06所有接入源配置变更
- 07所有 SSO 配置变更
- 08所有导出:CSV、PDF、定时报表派送
- 09所有批量删除与数据主体被遗忘权行使
保留期
2,555 天。与运营保留期相互独立。
RetentionConfig.AUDIT_LOG_DAYS 把链保留七年。Tenant.data_retention_days 的设计就是让运营数据独立可控,从而机器遥测可按可配置窗口清理,而审计链继续覆盖 SOC 2 CC2.2 证据期与财务相邻的保留规范。
合规映射
审计链回答了什么
SOC 2 CC6.1 与 CC7.2
防篡改记录与变更管理证据链。审计链就是您的 SOC 2 审计员在观察期内逐条复核的对象,以满足 CC6.1(逻辑访问)与 CC7.2(系统监控)。
ISO 27001 A.12.4
事件记录与操作员日志完整性。每一次管理员操作与异常都落到链上,带有时戳、归因,以及被操作的规范化载荷。
FDA 21 CFR Part 11 §11.10(e)
为电子记录提供安全、计算机生成、带时戳的审计轨迹。每个签名事件都具备可归因、同步、原始、准确与加盖时戳的属性,支持客户进行 21 CFR Part 11 的验证工作。
GDPR 第 30 条
处理活动记录。您的 DPO 可按需提取按租户切分的审计切片。audit_logs 中的个人数据仅限于用户标识与 IP 地址;更丰富的负载只携带重建事件所需的最小上下文。
欧盟 NIS2 指令
事件调查证据。成员国 CSIRT 与您自己的取证团队可以精确重建谁在什么时候做了什么,且这条链的完整性不依赖对 DBA 的信任。
验证器
被审计的审计验证
链验证使用应用持有的 HMAC 密钥,依据规范化负载重新计算每一行,返回带行数的 PASS,或在第一个断裂序列号上返回 FAIL。验证在 Haltless 一侧运行,因为该密钥从不离开应用。
节奏
当前可按需运行,通过门户或 API,凡能访问该租户审计日志的人均可触发。按日的定时验证将随平台 GA 一同上线。
结果
PASS 带行数,或者 FAIL 带链断裂处的第一个序列号。涉事的行本身作为证据保留。
源码
验证器的源码可供您的审计员逐行复核。我们会陪同到现场。
为什么这强于“我们有审计日志”
标准审计日志撑不过一个铁了心的操作员
普通审计表
易于追加,但可被修改。任何拥有数据库写权限的人都能改一行、删一行或插一行,而且没有任何数学信号表明此事发生。
WORM 存储
对删除有用,对插入或重排作用有限。存储层完整性抓不出那条本就不该存在的行。
Hash 链式日志(即本方案)
数学意义上的防篡改。在行级别检测篡改、删除与插入,以父级 hash 作为完整性锚点。
常见问题
如果某条 hash 不匹配,链会怎样?
验证器返回 FAIL,并指出链开始断裂处的第一个序列号。我们不会删除该行,证据要留下。失败本身作为独立的链条目被记录下来,并通过平台的事件响应路径浮现。
数据库管理员能否对链做手脚?
不能,除非他同时 (a) 绕过只追加触发器,(b) 拿到位于数据库之外的 HMAC 密钥,并且 (c) 为受影响租户重写之后的每一行。这三步本身都会留下取证痕迹。
能否为了 GDPR 被遗忘权删除某行?
audit_logs 中的个人数据已最小化为 user_id 与 IP。对用户记录行使被遗忘权不会移除审计历史,因为该处理建立在第 6(1)(c) 条法律义务与 (f) 合法利益之上。如果您的 DPO 要求,我们可以对 user_id 引用做假名化处理。
链会拖慢写入吗?
每次插入计算一次 HMAC,并读取一条前驱行,两者均有索引。在典型工作负载下成本可忽略,且验证器在主路径之外运行。

