• ryan-wong

    本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:

    - 我的帖子已经打上 开源推广 标签: 是

    - 我的开源项目完整开源,无未开源部分: 是

    - 我的开源项目已链接认可 LINUX DO 社区: 是

    - 我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是

    - 以上选择我承诺是永久有效的,接受社区和佬友监督: 是

    以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出


    在全都是ai项目的开源社区中,我做了一个这样的开源软件。 我不擅长写推广贴,但是有推广之心,大家也可以把这个看成技术分享。

    老板问:

    昨晚谁改了生产权限?

    运维查了一遍:

    审计日志里没有这条操作。

    老板又问:

    审计日志存在哪?

    我们数据库里。

    谁能改?

    还是我们。

    这就有点尴尬了。

    审计日志如果是内部审计肯定没问题,但真出了事故,仅仅拿出一张自己保管的日志表,很难说服另一方:

    - 你怎么证明这条记录不是后来补的?

    - 又怎么证明原来的记录没有被删掉或换掉?

    - 掌握日志的人,能不能同时改日志?

    TrustDB 是我对这个问题的一次尝试。

    它做的是把已经产生的关键审计记录,变成一条可以独立验证的证据链。

    项目地址:GitHub - ryan-wong-coder/trustdb: 高可信的存证系统 · GitHub

    官网:https://www.trustdb.ryan-wong.cn/

    一个数据有没有变动,不能只告诉第三方一句"请相信我们"

    一条比较完整的审计记录,通常会写明:

    - 谁执行了操作

    - 操作了什么对象

    - 执行了什么动作

    - 操作前后的状态摘要

    - 请求编号或任务编号

    - 记录产生时间

    - 业务附加信息

    这些内容可以来自业务系统、网关、发布平台或独立审计代理。

    TrustDB 首先计算记录正文的 SHA-256,再把内容摘要、长度、客户端身份、密钥编号、事件类型、时间、随机数和元数据组成一份客户端声明。

    接下来由审计记录的产生方签名。```
    input =
    "trustdb.client-claim.v1"
    || 0x00
    || canonical_cbor(claim)

    signature = Ed25519.Sign(client_private_key, input)
    ```这里使用确定性 CBOR,而不是随手生成的 JSON。

    因为数字签名面对的是字节,不是"看起来差不多的意思"。JSON 字段顺序、空格和数字写法不同,都会产生不同字节。确定性 CBOR 把编码固定下来,让签名方和验证方得到完全相同的输入。

    解码时还会拒绝重复字段、未知字段、CBOR tag、不定长编码、NaN、无穷大和尾随数据。

    做这么多主要是怕把两个解析器把同一段数据理解成不同意思。

    为什么签名前要写一串固定文字

    TrustDB 的客户端声明、服务端回执和全局日志树头,都使用 Ed25519 签名,但它们不能共用同一种签名语义。

    所以每类数据都有自己的签名域:

    - trustdb.client-claim.v1

    - trustdb.accepted-receipt.v1

    - trustdb.committed-receipt.v1

    - trustdb.signed-tree-head.v1

    客户端声明只能当客户端声明验证,接受回执只能当接受回执验证。

    不能拿着一份合法的客户端签名,换个外壳说它是服务端回执;也不能把旧格式里的签名塞进新格式继续使用。

    记录编号同样绑定了声明和客户端签名:```
    record_id =
    SHA-256(
    "trustdb.record-id.v1"
    || 0x00
    || claim_cbor
    || client_signature
    )
    ```因此,两条正文相同的审计记录,只要产生方、时间、随机数或签名不同,就会得到不同编号。

    内容相同,不代表是同一次操作。

    客户端签名,只能证明"记录是谁生成的"

    客户端签名能够证明:

    持有对应私钥的一方,签过这条审计声明。

    但它不能证明 TrustDB 服务端确实收到。

    所以服务端还会签发两种回执。

    接受回执表示:

    我验证了客户端签名,并且接受了这条记录。

    提交回执表示:

    这条记录已经进入某个 Merkle 批次,位置和批次根如下。

    "收到"和"已经进入可验证批次"被明确分开。

    如果客户端声称提交过,服务端却说没收到,可以检查接受回执;如果服务端声称记录已经入账,可以继续检查提交回执和 Merkle 证明。

    这比查一列 status=success 麻烦,但数据库里的状态可以被管理员直接修改,签名回执却不能在没有私钥的情况下重新生成。

    Merkle 树让一条记录牵动整批记录

    每条服务端记录先计算叶子哈希:```
    leaf = SHA-256(0x00 || canonical_cbor(server_record))
    ``两个节点合并时:``
    node = SHA-256(0x01 || left || right)
    ```0x00 和 0x01 用来区分叶子与内部节点,避免两种对象互相冒充。

    一条记录进入批次后,会得到一条通往批次根的证明路径。

    验证器重新计算这条路径,就能检查:

    这条审计记录是否真的属于这个批次根。

    有人修改记录正文,内容哈希会变;客户端声明和记录编号随之变化;Merkle 叶子、路径和批次根也会跟着变化。

    想只改一条审计记录,却保持原来的批次根不变,需要找到 SHA-256 碰撞。

    这已经不是改一行数据库的问题了。

    如果管理员把整批记录一起重做呢

    只做一层 Merkle 树还不够。

    服务器管理员可以丢掉旧批次,重新生成一批记录,再拿出一个新的批次根。

    因此 TrustDB 会把每个批次根继续写入全局透明日志。

    全局日志本身也是一棵持续增长的 Merkle 树。每次追加后,服务端都会生成带签名的树头 STH,里面有:

    - 当前树的大小;

    - 当前根哈希;

    - 日志编号;

    - 节点编号;

    - 生成时间;

    - 服务端签名。

    这里有两种证明。

    包含证明回答:

    这批审计记录是否真的进入了全局日志?

    一致性证明回答:

    今天的全局日志,是否由昨天的日志继续追加得到?

    假设昨天有一万个批次,今天有一万零三个。一致性证明检查的不是"今天这棵树能不能算通",而是昨天那一万个批次有没有被原样保留下来。

    这样就不能趁夜里换掉一段历史,再重新生成一棵结构正确的新树。

    如果服务端给不同的人看不同历史呢

    透明日志仍然可能出现分裂视图。

    管理员给甲看一份 STH,给乙看另一份 STH,两边单独验证都能通过,但看到的历史不同。

    所以,TrustDB 可以把 STH 的全局根继续提交给 OpenTimestamps。

    提交出去的只是一个 32 字节哈希,不包含审计记录正文,也无法从中还原业务数据。

    证明关系变成:```
    审计记录
    ↓ SHA-256
    客户端声明
    ↓ 客户端 Ed25519 签名
    服务端回执
    ↓ 批次 Merkle 证明
    批次根
    ↓ 全局透明日志
    签名 STH
    ↓ OpenTimestamps
    外部时间见证
    ```OpenTimestamps 日历服务器会先返回待确认的时间戳证明,后续还可以升级到比特币区块见证。

    它能帮助证明的是:

    与这条审计记录相连的全局根,最迟在某个时间以前已经存在。

    它不能证明审计内容本身符合事实,也不表示审计记录正文被上传到了外部网站。

    验证器不会相信证明文件自报家门

    TrustDB 可以把客户端声明、服务端回执、Merkle 路径、全局日志证明、STH 和外部锚定放进一个 .sproof 文件。

    文件里虽然有 proof_level,但验证器不会因为它写着 L5,就直接认定达到 L5。

    验证时会重新计算:

    - 审计记录正文的长度和 SHA-256;

    - 客户端声明的确定性 CBOR;

    - 客户端 Ed25519 签名;

    - 记录编号;

    - 服务端接受回执;

    - 服务端提交回执;

    - Merkle 叶子和批次根;

    - 批次根到全局 STH 的包含证明;

    - STH 的服务端签名;

    - 外部锚定与 STH 根是否一致。

    能算到哪一层,才算达到哪一级。

    证明文件说自己是 L5,和嫌疑人说自己无罪一样,只能作为提示,不能作为结论。

    它能防事后改账,不能防一开始就不记账

    这是审计场景里必须说明的边界。

    如果某次操作从一开始就没有进入审计采集端,TrustDB 不可能凭空知道它发生过。

    如果审计客户端私钥已经被控制,攻击者也可以签署新的虚假记录。

    如果业务系统把错误的操作者身份交给审计端,密码学只会忠实地保护这个错误身份。

    TrustDB 主要解决的是:

    一条审计记录一旦被签名、接受并进入证明链,后来还能不能悄悄修改或替换。

    至于"是否每次操作都生成了审计记录",仍要依靠独立采集、连续序号、权限隔离和业务制度。

    一个防改,一个防漏,不是一回事。

    最后

    TrustDB 目前使用:

    - SHA-256;

    - Ed25519;

    - Core Deterministic CBOR;

    - RFC 6962 风格 Merkle 树;

    - 签名树头;

    - 包含证明和一致性证明;

    - OpenTimestamps 外部锚定;

    - 可离线验证的 .sproof v1。

    代码、证明格式和测试向量全部公开。

    如果佬友们做过操作审计、发布审计、权限变更审计、数据流通记录或者密码学、区块链研究,欢迎从实际情况里挑问题,提issue

    注:使用大模型从邮件中复制后重新md格式,当前版本无润色内容(确信)

    图片

    主楼
  • Ys_Ltr

    只看了前面欸,但是区块链技术不就能解决防篡改的问题吗。是不是某些旧技术完全失传了

    #1
  • ryan-wong

    区块链有成本和性能问题,和日志的短时大量输出存在张力

    此项目最后依然应用了区块链作为外置挂载证据,但是大幅压缩了上链篇幅

    #2
  • 过期绿坝粮

    简单来说就是区块链签名?

    #3
  • ryan-wong

    简单来说应该是上链前的高吞吐压缩,同时除了单独证明以外,还附带了一致性证明回答,证明自己是在一个连续的历史里进行的追加,而非孤立生成

    #4
  • ryan-wong

    补充:单靠区块链,很难实现高吞吐的日志存证,此项目相当于是高度节省的链前压缩

    #5
  • airline233

    听上去和这个是一样的

    6.43 复制打开抖音,看看【老头们的快乐生活的作品】王处祝大家六一快乐 你们就说王处这个大春链,有没有… https://v.douyin.com/RA0LeBNBGrU/ [[email protected]](https://feedbbs.com/cdn-cgi/l/email-protection) mQX:/ 06/15 :1pm

    https://www.douyin.com/video/7646082737879902705

    人家还更节约算力,只算md5而非SHA256

    #6
  • ryan-wong

    那到不是,因为merkle tree保留了完整的证明语义,具体证明语义可以看readme,其实merkle tree压缩后上链是很多存证系统成熟的实现方案,但是我进一步整理了证明语义,并且将上链调用压缩到固定时间窗口常数级,这个可以省下大量开销

    #7
  • ryan-wong

    我可以尽可能的善意回复你,但是你的这个视频蛮有侮辱性质的

    #8

发表回复

登录后回复