返回 AIPA 笔记
AIPA Day 75

审计轨迹 II — 不可篡改:哈希链、监管要件与落盘

审计轨迹 II — 不可篡改:哈希链、监管要件与落盘

2026-08-28
immutable-audithash-chaintamper-evidentmodel-risk

日期: 2026-08-28 阶段: Phase 3 - AML 调查 Copilot 标签: #immutable-audit #hash-chain #tamper-evident #model-risk

核心问题

Day 74(审计轨迹 I)解决了「记什么」——把 Copilot 的每一步推理、每一次检索、每一处证据引用结构化成可回放的 trace。今天解决「记下来后还能不能被改」。这不是同一个问题的延续,而是一个性质完全不同的问题:trace 可观测性服务于「我们自己排错」,而审计轨迹的不可篡改性服务于「监管和法庭相信我们没改」。前者怕看不见,后者怕被人(包括内部人、包括 DBA)偷偷动手脚。

三个子问题:(A) append-only + 哈希链到底怎么把「改一条就被发现」变成密码学事实——给出链结构和篡改检测算法;(B) 为什么金融合规强制要求不可篡改,而不是「最好有」——拆 SR 26-2(刚替代 SR 11-7)与 EU AI Act Article 12/50 的法条要件;(C) 这套东西怎么落到本项目的 src/aml 审计模块与 Langfuse 后端。

贯穿全篇的一个判断:审计 trail 本身是合规架构组件,不是日志。 把它当 console.log 的升级版来设计,是 AML 产品最常见、也最致命的架构误判。

关键内容

A. append-only + 哈希链:把「改一条就被发现」变成密码学事实

不可篡改不是靠「数据库设了只读权限」实现的——权限可以被有权限的人绕过(root DBA、被攻陷的服务账号)。真正的不可篡改是密码学的:让任何一处修改在数学上必然留下可检测的痕迹,无论改的人有多大权限

机制是哈希链(hash chain):把审计事件按时间排成一条链,每条记录里嵌入「前一条记录的哈希」。这样每条记录的哈希都依赖它前面所有记录——改动链中任意一条,它之后所有记录的哈希全部失配,且攻击者无法「只改一条而让后面对得上」,除非他重算并替换从该点到链尾的全部记录(且若链根已外部锚定,连这条路都被堵死)。

一条审计事件的结构(VeritasChain VCP 协议口径,2025-12 / 2026-05):

AuditEvent {
  header  { event_id, trace_id, seq, timestamp_iso, event_type }   // 谁、何时、第几条、什么动作
  payload { ... 业务字段(本项目:caseId / typology / citedTxIds / judgeScore / 复核决定)... }
  security {
    prev_hash   // 链:前一条的 event_hash(创世块 = "0"×64)
    event_hash  // = H( canonical(header) ‖ canonical(payload) ‖ prev_hash )
    signature   // 可选:对 event_hash 的 Ed25519 签名(非否认性)
  }
}

哈希计算用 SHA3-256(或 SHA-256),输入是**规范化(RFC 8785 JSON Canonicalization)**后的 header‖payload‖prev_hash 拼接——规范化是必须的,否则同一语义的 JSON 因键序/空格不同会算出不同哈希,给攻击者留下「换个等价表示就改了内容却不破链」的缝隙。

篡改检测算法就是一次顺序重放(verification walk):

function verifyChain(events):
    prev = "0"*64                       # 创世哈希
    for i, e in enumerate(events):
        if e.security.prev_hash != prev:                 # ① 链断:被删/被插/被重排
            return TAMPER, f"chain break at #{i}"
        recomputed = H(canon(e.header) ‖ canon(e.payload) ‖ prev)
        if recomputed != e.security.event_hash:          # ② 内容被改:payload 动过
            return TAMPER, f"content altered at #{i}"
        if e.header.seq != i:                             # ③ 序号缺口:检测「整段删除」
            return TAMPER, f"seq gap at #{i}"
        prev = e.security.event_hash
    return OK

三类检测对应三种攻击:①prev_hash 失配抓删除/插入/重排;②重算哈希失配抓篡改某条内容;③seq 连续性抓成段删除(光靠哈希链查不出「把尾部 N 条整体砍掉」——链仍然自洽,所以必须独立校验序号单调连续,这是哈希链单独用时的盲区)。

反直觉洞察①(哈希链能抓「改」,抓不住「整段删」):直觉以为哈希链 = 万能防篡改。错。链只保证「留下的记录内部一致」——把链尾整段截掉,剩下的部分依然是一条合法自洽的链,verifyChain 的 ①② 检测全过。要抓「截断」必须叠加严格递增序号 + 外部锚定(把链根 Merkle root 定期公证到一个你不控制的地方)。这就是 VeritasChain 那句「把 batch 哈希成 Merkle 树,把根锚定到你管不着的地方(anchor the root somewhere you don't control)」的全部意义——没有外部锚点,append-only 只防得住改、防不住删。

更进一步,tamper-evident(防篡改可检测)≠ tamper-proof(不可篡改)。哈希链给的是前者:它不能阻止有 root 权限的人物理删库,它保证的是任何改动都会留下数学可证的痕迹。VeritasChain v1.2(2026-05)明确把协议范围限定为「可验证地记录算法/AI 系统决定了什么、做了什么」,并坦承做到 tamper-evident 已经够难——「Append-only is the easy part」,真正难的是运营:链会因崩溃中途写入而断、GDPR Article 17 删除权与 append-only 直接冲突(其解法是 crypto-shredding:字段用主体专属密钥加密、哈希只算密文、删除时销毁密钥使明文不可恢复而链仍可验证)。

B. 为什么金融合规强制不可篡改:SR 26-2 与 EU AI Act 的法条要件

先纠正一个时效性陷阱(关键):很多 2024-2025 的资料把 SR 11-7(美联储/OCC 2011 模型风险管理指引)当作 AML 模型治理的活法规引用。2026-04-17,美联储 / OCC / FDIC 三家联合发布 SR 26-2 / OCC Bulletin 2026-13《Revised Guidance on Model Risk Management》,明确取代(supersede and replace)SR 11-7(2011-04-04)以及 SR 21-8(2021 的 BSA/AML 模型风险联合声明)。换言之,到今天(2026-08)引用「SR 11-7」描述现行义务已经过时,应引用 SR 26-2(更明确的风险分级 / 原则导向,主要适用于 >$300 亿资产的机构)。本笔记按 SR 26-2 口径表述——但其对审计可追溯的核心要求与 SR 11-7 一脉相承,下文标注法规状态。

模型风险管理三道防线(SR 11-7 立、SR 26-2 沿用的经典结构)要求模型生命周期的每一个动作可重建、可追溯——SR 26-2 配套解读(Databricks 2026、Davis Polk 2026-04)指出:从数据预处理、超参选择到生产部署的每一步都必须可记录、可复现,机构应优先采用提供「不可变审计轨迹 + 版本化产物 + 任意时点状态重建」能力的基础设施。对一个自主出 SAR 草稿的 AI agent,监管的潜台词是:你说这份 SAR 是基于这 7 笔交易、这条类型学、这个 judge 分判出来的——你拿什么证明事后没人改过这个推理链? 答案只能是不可篡改的审计轨迹。

EU AI Act Article 12(Record-keeping,记录留存) 把这条从「最佳实践」变成「成文法义务」:高风险 AI 系统必须技术上支持在系统全生命周期内自动记录事件(logs),用于 (a) 识别可能致风险/重大修改的情形、(b) 支撑投放后监测、(c) 监控运行;日志至少留存 6 个月,「自动」意味着系统自己生成、手工文档不算数

法规/状态对审计轨迹的硬要求与不可篡改的关系
SR 26-2 / OCC 2026-13(2026-04-17 生效,替代 SR 11-7/SR 21-8)模型生命周期每动作可重建、可追溯;三道防线独立验证「可重建」要求记录不可被悄悄改
EU AI Act Art.12(高风险义务经 Digital Omnibus 临时协议 2026-05-07 推迟至 2027-12-02全生命周期自动记日志,≥6 个月留存「自动 + 全生命周期」=审计轨迹强制
EU AI Act Art.50(透明义务 2026-08-02 生效AI 生成内容须标注 + 机器可读标记;人工复核+担责可获豁免空间SAR「AI 起草」事实必须落审计轨迹
MiFID II RTS 25 / MiFIR(生效,类比)时钟同步至微秒、订单记录多年留存篡改不可检测即不合规

注意三处法规状态差异,不能混为一谈:SR 26-2 已生效;Article 12 这类高风险义务被 2026-05-07 的 **Digital Omnibus 临时协议(provisional agreement,尚未最终通过)**整体推迟到 2027-12-02;而 Article 50 的透明义务仍按原计划 2026-08-02 生效。对本项目最直接的当期约束其实是 Article 50——SAR 由 AI 起草这一事实,必须有审计记录证明「已标注 + 有人工复核担责」(Article 50 给「人工复核 + 可识别编辑责任人」留了豁免空间),而这份证明本身就得放在不可篡改的轨迹里,否则「我们确实标注并复核了」无从自证。

反直觉洞察②(审计轨迹是合规架构组件,不是日志):工程直觉把审计写成「加强版日志」——多打几行、配个集中式日志库就完事。这是把举证责任倒置了。日志服务的是「我们想看时能查到」;审计轨迹服务的是「监管/法庭不信我们时,我们能用密码学自证清白」。差别是举证方向:日志是给自己看的便利,审计轨迹是给对家看的、且必须在对家假设我们会作弊的前提下仍然成立。所以它的设计约束完全不同——日志可改可删(滚动覆盖很正常),审计轨迹一旦写入就任何人(含管理员)都不能无痕修改。把审计当日志做,等于在合规上裸奔:出事时你说「我们记了」,监管问「你怎么证明没改」,你哑口无言。它是和鉴权、加密同级的架构组件,不是 observability 的附属品。

C. 落盘:Langfuse 不可变模型 + 本项目审计模块的双层设计

本项目不重新发明轮子。后端复用 Day 25 已选型的 Langfuse 自托管,而 Langfuse 2026 的数据模型恰好天然适配审计:因为 OpenTelemetry span 是不可变的,Langfuse 摄取的所有 observation 也不可变(Langfuse 2026-03「Simplify for Scale」博客明确:迁向「宽表、(基本)不可变、append-only、无 join 的 ClickHouse 表」——写入一次绝不修改,消除去重)。这正是 Day 25 选 ClickHouse 列存的额外红利:不可变 + append-only 是审计的前提,而 Langfuse v3 的存储模型已经把它当成性能优化的副产物送上门了

但 Langfuse 的不可变只到「记录写入后不被业务逻辑改」这一层——它不自带哈希链,无法对抗「绕过应用、直接动 ClickHouse 行」的内部威胁。所以本项目设两层

职责实现对抗的威胁
L1 不可变摄取trace/observation 写入即不可变、append-onlyLangfuse 自托管(Day 25),OTLP /api/public/otel业务逻辑误改、并发覆盖
L2 哈希链 + 锚定审计事件链式哈希、序号连续、链根定期外部锚定src/aml 新增审计模块(A 节算法)root DBA / 攻陷服务账号 / 整段删除

L2 是本项目要落的代码。设计决策:审计事件只链关键合规节点,不链每个 token——把 Copilot 全程里「合规上需要自证」的节点(类型学判定、证据引用集、judge 打分、人工复核决定、SAR 提交)抽成 AuditEvent 上链;高频低价值的 token 级 span 留在 Langfuse L1 不上链(否则链长爆炸、Merkle 锚定成本失控)。

设计要点/决策表

要点决策理由
不可篡改实现哈希链(SHA3-256)+ 严格递增 seq + 链根外部锚定仅 append-only 防不住「整段删」(洞察①)
哈希输入规范化RFC 8785 JSON Canonicalization 后再哈希防「等价 JSON 重排」绕过检测
tamper-evident 定位目标是「改动可检测」非「物理不可改」tamper-proof 不可达;可检测已满足举证
活法规锚点SR 26-2(2026-04-17 替代 SR 11-7) + EU AI Act Art.12/50SR 11-7 已被取代,引用须更新
当期最硬约束Article 50(2026-08-02 生效)AI 起草标注+复核担责Art.12 高风险义务已推迟至 2027-12-02
后端Langfuse 自托管(不可变 observation)做 L1Day 25 已选型,2026-03 起天然 append-only
链什么只链合规关键节点,token 级留 L1全量上链 Merkle 成本失控
审计 vs 日志当合规架构组件设计,非 observability 附属举证方向相反(洞察②)

对本项目的落地

  • 新建 src/aml/audit/auditTrail.ts:导出 AuditEvent 类型(header/payload/security 三段,对齐 A 节结构)、appendEvent(chain, payload) → AuditEvent(取上一条 event_hashprev_hash、seq 自增、算 event_hash)、verifyChain(events) → { ok, breakAt?, reason? }(实现 A 节三类检测:链断/内容改/seq 缺口)。哈希用 Web Crypto crypto.subtle.digest('SHA-256', ...)(浏览器/Node 通用,SHA3 待 polyfill 评估);canonical 序列化用稳定键序 JSON。诚实标注:外部锚定(Merkle root 公证到不可控存储)为 P3 上线后运营动作,W11 仅落链 + 自校验,锚定留接口 anchorRoot() 占位不实做。
  • 审计事件源:在现有产物链路上挂钩——typology.assessCase 出 topTypology 时、sarDraft.draftSar 出 citedTxIds 时、(P3)judge 打分时、人工复核决定时各 append 一条 AuditEvent,payload 复用现有结构(caseId / topTypology / citedTxIds / sections.length)。这条复用 evalChecks.ts 已沉淀的「可溯源」字段(cited_tx_exist 检查的 citedTxIds),审计与 eval 共享同一证据集,不另造一套。
  • 与 Langfuse 的分层:L1 走 Day 25 的 OTLP 端点(observation 不可变);L2 哈希链是本项目自有审计模块,二者用 trace_id 关联(Day 24 的 execution_id / trace_id 贯穿口径),实现「Langfuse 里看一步步推理 → 审计链里验这步没被改」。
  • Article 50 落地:SAR 草稿的「AI 起草 + 已人工复核 + 复核人标识」三个事实,作为 AuditEvent 的强制 payload 字段上链——sarDraft.ts 头注已有「须经调查员人工复核」声明,本模块把这句从文档承诺变成可验证的链上记录,兑现 Article 50(2026-08-02 生效)的标注+担责要求。
  • 诚实标注纪律:审计模块头注写明——L1 不可变依赖 Langfuse 自托管(计划态后端,当前静态站无后端);L2 哈希链是纯 TS 可单测的当前实现;外部锚定/签名密钥管理为运营态,W11 不实做。不把「写了哈希链函数」夸成「上了不可篡改审计系统」。

参考资料

  1. VeritasChain — Building Tamper-Evident Audit Trails for Algorithmic Trading: A Developer's Guide(哈希链结构 event_hash=H(header‖payload‖prev_hash)、SHA3-256/SHA-256、RFC 8785 规范化、verifyChain 重放检测、RFC 6962 Merkle 锚定、Ed25519 签名、genesis=0×64)(2025-12-30)
  2. VeritasChain — Making an Audit Trail Immutable Is Easy. Operating One Is Not.(append-only 是易事、运营才难;链断恢复需 Emergency Override 双人审批;crypto-shredding 解 GDPR Art.17 删除权 vs append-only 矛盾;tamper-evident≠tamper-proof;锚定根到不可控存储)(2026-05-31)
  3. The Fed — SR 26-2: Revised Guidance on Model Risk Management(2026-04-17 发布,supersede & replace SR 11-7 (2011-04-04) 与 SR 21-8 (2021 BSA/AML 模型风险声明);风险分级/原则导向;>$300 亿资产机构最相关)(2026-04-17)
  4. OCC — Bulletin 2026-13: Model Risk Management: Revised Guidance + Davis Polk / Sullivan & Cromwell 解读(生命周期每动作可重建可追溯)(2026-04)
  5. EU Artificial Intelligence Act — Article 12: Record-keeping(高风险系统全生命周期自动记日志、≥6 个月留存、自动非手工)+ artificialintelligenceact.eu (持续;高风险义务经 Digital Omnibus 临时协议 2026-05-07 推迟至 2027-12-02)
  6. Langfuse — Simplifying Langfuse for Scale(OTel span 不可变 → 所有 observation 不可变;迁向宽表、append-only、无 join 的 ClickHouse 表;写一次不改、消除去重)(2026-03-10)
  7. Databricks — Model risk management in 2026: A banker's guide to the revised interagency guidance(不可变审计轨迹 + 版本化产物 + 任意时点状态重建为基础设施优先项)(2026)

SOTA 检查 (2026-08-28 / 复核基线 2026-06-11)

  • 哈希链 + 外部锚定仍是不可篡改审计的事实标准(2026-08):NIST 口径(WORM 存储 + 每条记录含哈希链 + 受信时间戳 + 读写/删权限分离)、VeritasChain VCP v1.2、各金融审计平台一致。SHA3-256/SHA-256 哈希链 + Merkle 根锚定无替代方法论出现。
  • 法规时效性是本篇最大更新点(务必记牢)SR 11-7 已于 2026-04-17 被 SR 26-2 取代——任何仍以 SR 11-7 为活法规的资料(含本项目早期笔记里把 SR 11-7 标「经典监管」的表述)须更新为 SR 26-2,并标明其同时取代了 SR 21-8(BSA/AML 模型风险声明)。核心可追溯要求不变,引用名变了。
  • EU AI Act 三处状态必须分清:Art.50 透明义务 2026-08-02 已生效(本项目当期最硬约束);Art.12 等高风险义务经 Digital Omnibus 临时协议(2026-05-07,未最终通过)推迟至 2027-12-02——写「Article 12 现已强制」是错的,须标「拟于 2027-12 生效」。
  • Langfuse 不可变模型是新增红利:2026-03 起 Langfuse 明确走「不可变 observation / append-only / 无 join」——这使 Day 25 的后端选型额外获得审计友好属性,无需为审计另选库;但 Langfuse 不自带哈希链,L2 防内部篡改仍须本项目自实现。
  • 过时认知警示:①「append-only 即不可篡改」过时——防不住整段截断,须配 seq 连续性 + 外部锚定(洞察①);②「审计 = 加强版日志」过时——举证方向相反,是合规架构组件(洞察②);③「SR 11-7 是现行模型风险法规」过时(已被 SR 26-2 取代)。
  • 待跟踪:Digital Omnibus 是否如期最终通过、Annex III 高风险义务 2027-12-02 时点是否再变;后量子签名(VCP 把 PQ 从 reserved 升 experimental,Ed25519 仍默认)何时成为审计签名默认;W11 落地后外部锚定(Merkle root 公证)选哪种不可控存储。