跳到正文

安全

威胁模型

go-sequent 的威胁模型:资产、信任边界、威胁与缓解、明确的非目标,以及基于该库构建应用时的评审清单。

威胁模型

这是 go-sequent 面向生产加固的威胁模型,修订于 2026-10-26。它遵循锁定决策:hash chain(按聚合串联事件哈希,用来发现篡改)完整性、不做事件签名、仅 Tombstone 式 GDPR(不删历史事件,而是追加一条标记事件,让读模型不再暴露个人数据),以及无 HTTP 暴露面的纯库。

资产

资产 说明
事件库(events、commands) 事实来源。正常运行时只追加。
唯一键与幂等键(同一业务键重复提交只成功一次) 重试下的正确性
读模型表 可丢弃、可重建(Projector 把事件写成查询用读模型)
Outbox(事务内记待发消息,事后轮询投递) 至少一次投递的缓冲
快照 性能缓存,以带版本的 JSON 存储
Payload 中的 PII 通过应用提供的 PayloadProtector 加密。GDPR 删除只通过 Tombstone 完成。

信任边界

  1. **客户端到应用。**认证发生在你的应用里。go-sequent 只把 Meta.AuthContext 与 UserID 当作不透明的声明。没有经过验证的认证,绝不能信任客户端设置的 UserID。
  2. **应用到 CommandService。**AuthZ 钩子(security.Authorizer)作为 Filter 或 Middleware,在 Handler 之前运行。
  3. **CommandService 到 PostgreSQL。**单写者主库。事件带 hash chain,你再加上数据库 ACL 与备份。没有事件签名。
  4. **SyncInTxn(写事务内同步更新读模型)Projector。**对账本而言,它们与追加在同一个写事务里。
  5. **Outbox 轮询器。**投递是至少一次,因此 Handler 必须可重复处理(Idempotency)。

威胁与缓解

威胁 缓解
API 重放或重复提交 租户级幂等键与防御式 Handler
通过伪造命令提权 AuthZ 中间件。敏感命令类型使用 DenyByDefault。
事件库被篡改 hash chain 校验,加数据库控制,可选 WORM 存储。不做签名。
跨租户访问 meta 与行上的 tenant_id、tenancy.MatchAggregateTenant、tenancy.UniqueScope
日志与 trace 中的 PII 脱敏、PayloadProtector,绝不记录密钥
Projector SQL 注入 只使用参数化的 Persistor
Workflow 或 Outbox 重复执行 幂等命令,加 Outbox ack 与去重
快照投毒 带版本的 JSON,由事件重新生成
GDPR 删除 Tombstone(标记清除事件)加 Projector 清理。加密销毁(crypto-shredding)不作为产品功能。
导入 Ruby Sequent dump 永不

关于 hash chain

hash chain 能发现对已存储事件的修改,但无法阻止拥有写权限的人从修改点起重写整条链。因此要把它与数据库访问控制、受限的恢复权限和备份配合使用;如果监管要求,再加 WORM 存储。见 EventStore。

关于顺序

按当前的中间件顺序,幂等重放会在 AuthZ Filter 之前就得到响应。如果重放的命令也必须重新授权,请把授权写成在幂等之前运行的中间件。见 AuthZ 与 Idempotency。

明确的非目标

  • 事件签名。hash chain 是框架内的完整性机制。
  • 把加密销毁作为 GDPR 路径。
  • 本仓库内的 gRPC 或 HTTP 服务。

应用评审清单

  • 每一种会修改状态的命令类型都安装了 authorizer。
  • 命令上总是设置租户,分区键以租户为作用域。
  • 金额字段使用 types.Money,绝不使用 float64。
  • 账本余额由 SyncInTxn 处理,Outbox 只用于异步扇出。
  • 已完成备份恢复演练,见 备份与恢复。
  • 对任何改名的事件 payload 都注册了 upcaster。

相关

文档随 go-sequent 源码更新;若与 API 不一致,以源码为准。