本文深入探讨了 Merkle 树中的第二原像攻击,解释了其原理以及如何利用中间节点来伪造叶子节点的证明。文章还详细介绍了四种有效的防御策略,包括使用不同的哈希函数、对叶子节点进行双重哈希、添加前缀以及限制叶子节点的长度,并分析了这些方法在实际应用中的适用性。
假设我们有一个数组 ["L1", "L2", "L3", "L4"],只是为了保持 Merkle 树的简单性。考虑到我们将使用 keccak256 哈希,每个值在使用前都会被哈希处理,作为叶子节点。所以树的叶子节点看起来像这样:
接下来,我们将逐步哈希这些哈希值的连接,逐步构建树。
接下来,我们将连接最后两个节点并对其进行哈希以获得根,瞧,我们有了一个完整的 Merkle 树。
Merkle 证明的验证函数接受两个东西:一个是非哈希的叶子节点(假设我们要证明存在的叶子节点是 L1),以及一个证明数组,该数组具有使用叶子节点形成根的中间节点。它首先对叶子节点进行哈希,然后构造根。如果从证明和 L1 导出的根与存储的根匹配,则验证通过。值得注意的是,证明的长度仅为 log₂(叶子数量),这使得它非常紧凑 —— 例如,证明一个具有 100 万个叶子的树的成员资格只需要 20 个哈希,这就是 Merkle 树对于大型数据集如此高效的原因。
rust 中 验证函数的函数签名如下所示:
fn verify(leaf: &[u8], proof: &[[u8; 32]]) -> bool
为了证明 L1,我们需要树中的以下节点:[hash(L2), hash(b)],标记为红色。
从给定的叶子节点和证明节点生成根的路径如下所示
现在考虑一下我们如何证明一个甚至不存在于此 Merkle 树中的叶子节点。如果我们提供 a 作为叶子节点(其中 a 是 hash(L1) 和 hash(L2) 的连接),并提供 [hash(b)] 作为证明数组呢?验证将首先哈希 a 以获得 hash(a),然后将其与 hash(b) 连接并再次哈希以获得相同的根。即使 a 从未作为叶子节点存在,这也会成功,它实际上是一个中间节点。
我们能够为一个甚至不存在的叶子节点获得相同的根。
假设我们使用 Keccak256 哈希节点,使用 SHA256 哈希叶子节点。让我们看看在这种情况下会发生什么。采用与上面相同的例子,其中:
leaf = a -> L1 的哈希和 L2 的哈希的串联
proof = [hash(b)] -> hash(L3) 的哈希和 hash(L4) 的哈希的串联
现在,由于 a 是作为叶子节点提供的,验证函数将尝试使用 SHA256 哈希它,而不是在原始树中使用 Keccak256 进行哈希,从而导致完全不同的根。因此,第二原像攻击将失败。
这与第一个解决方案类似。唯一的区别是,我们不是使用不同的哈希,而是将叶子节点哈希两次,内部节点哈希一次。假设我们使用 Keccak256 哈希。树看起来像这样:
现在,如果我们尝试将 a 作为伪造的叶子节点输入,验证器将对其哈希两次,从而导致不同的根和不同的哈希。
在这种情况下,哈希将保持不变,也不会有双重哈希,但是在哈希之前,我们将添加一些前缀。叶子节点和节点的前缀将有所不同。例如:
叶子节点前缀 = "0x01"
节点前缀 = "0x02"
树看起来像这样:
并且给定 a 作为叶子节点,它将使用前缀 0x01 而不是 0x02 哈希它,从而导致不同的叶子节点。
这就是 OpenZeppelin 在 Merkle 树库中防止此攻击的方式。考虑到 Solidity 中的哈希为 32 字节,在每个级别连接两个 32 字节的哈希会导致 64 字节,然后将这 64 字节哈希回 32 字节。因此,如果我们在哈希之前限制叶子节点只能为 32 字节,则可以防止此攻击。方法如下:考虑以下仅接受 32 字节作为叶子节点的树。现在,如果攻击者尝试提供 a(它是 hash(L1) 和 hash(L2) 的连接,总计 64 字节)作为叶子节点,它将被拒绝,因为它超过了 32 字节的限制,从而防止了攻击。
第二原像攻击利用了 Merkle 树中叶子节点和内部节点哈希之间缺乏区分,从而允许攻击者伪造不存在数据的证明。幸运的是,存在多种有效的缓解措施:使用不同的哈希函数、双重哈希叶子节点、添加前缀或限制叶子节点大小。所有这些方法都具有相同的核心原则:确保内部节点不能伪装成叶子节点以产生相同的根。
在生产环境中实施 Merkle 树时,重要的是要理解并非所有预防技术都可以有意义地组合。以下是一个比较:
关键的见解是,结合两种从根本上改变哈希工作方式的技术(单独的哈希函数 + 双重哈希)是多余且浪费的,而前缀是普遍兼容的,因为它只是添加元数据而不改变哈希机制本身。
我们将继续发布与金属密切相关的构建者和审计员的实用、高信号深度研究。 关注 Adevar Labs 在 X 上 和 LinkedIn 以保持更新。
- 原文链接: adevarlabs.com/blog/prev...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!