当 Merkle 树中的中间节点被当作叶子节点提交时,就有可能发生 Merkle 树的 第二原像攻击(second preimage attack)。
这种攻击的名称具有很大的误导性,因为它暗示哈希值存在第二原像。现代哈希函数并不存在多个(可计算的)原像。
这种攻击更好的名称应该是“节点伪造叶子攻击”(node as leaf attack)或“缩短证明攻击”(shortened proof attack)。
前置知识
我们假设读者已熟悉 Merkle 树和 Merkle 证明。
符号说明
我们用 h(x) 表示 x 的哈希值。h(x + y) 是 x 和 y 拼接后的哈希值。在本文中,我们将重点使用 keccak256 作为我们的哈希函数;选择一个具体的函数有助于让后文中的一些推理更加清晰。但请记住,本文中的思路适用于任何哈希函数。我们用 ℓ 表示 Merkle 树的叶子节点。第 i 个叶子节点表示为 ℓᵢ。
攻击示例
假设我们希望为下树中的叶子节点 2(ℓ₂)创建一个证明。该叶子节点为 ℓ₂,证明将是 \[h(ℓ₁), h(b), h(f)\]。值 a、b、c、…、g 是其子节点值的拼接。如果 h(g) 等于 Merkle 根(Merkle root),我们就会接受该证明。

该证明将是 \[h(ℓ₁), h(b), h(f)\],在图中以绿色标出。到根节点的证明过程为:
h(a) = h(h(ℓ₁) + h(ℓ₂))
h(e) = h(h(a) + h(b))
h(g) = h(h(e) + h(f))
return root == h(g)
第二原像攻击
如果攻击者提供 a 作为叶子节点,并提供 \[h(b), h(f)\] 作为证明,会发生什么呢?

合约会将 a 视为一个叶子节点,其中 a = h(ℓ₁), h(ℓ₂))。如果证明是 [h(b), h(f)],该 Merkle 证明将被接受为有效。
从本质上讲,如果一个 Merkle 证明是有效的,那么只要我们将原始证明中的第一个值作为叶子节点传入,它的缩短版本也会是有效的。
因此,攻击者将提供一个“叶子节点”以及一个合约会接受的证明,但给定的“叶子节点”并不是原始 Merkle 树中的叶子节点!
那么我们该如何防止这种情况发生呢?
OpenZeppelin 的警告
在 OpenZeppelin 的 Merkle 树库中,我们在注释里看到了相关警告以及一些解决方案。我们将在接下来的小节中解释他们的解决方案。

以下两个小节将解释这两种解决方案是如何防止该问题的。
该攻击需要 64 字节的叶子节点
要使此攻击生效,攻击者必须传入中间节点的原像,而不是它的哈希值。这意味着它必须将 a 作为叶子节点传入,而不是 hash(a)。由于 Solidity 使用 32 字节的哈希值,a = h(ℓ₁) + h(ℓ₂) 的长度将为 64 字节。
如果合约不接受 64 字节的叶子节点作为输入,那么该攻击将无法生效。
也就是说,如果叶子节点输入的长度不是 64 字节,就不可能将 h(ℓ₁) + h(ℓ₂) 作为伪造的叶子节点值传入。
使用不同的哈希处理叶子节点作为防御
如果我们的应用程序出于某种原因需要接受 64 字节的叶子节点,那么我们可以通过对叶子节点和证明使用不同的哈希函数来防止攻击。
也就是说,在对叶子节点进行首次哈希运算时,我们使用与计算根节点哈希不同的哈希函数。这将防止攻击者把中间节点“重构”成叶子节点。以上图为例,攻击者使用 h(a) 来创建伪造的“叶子节点”。然而,如果叶子节点是通过 h’(a) 处理的,那么该中间值就无法被重构。
OpenZeppelin 使用双重哈希作为“不同的哈希”
OpenZeppelin 并没有使用会消耗更多 gas 的不同哈希函数(例如通过 precompile),而是简单地对叶子节点进行两次哈希运算。即 h’(x) = h(h(x))。
我们使用了 绿色下划线 来标识从底层数据构造叶子节点时进行两次哈希运算的位置

请注意,该 library 并不强制你应用两次哈希——事实上它根本不强制你对叶子节点进行哈希运算!不对叶子节点进行哈希运算可能会引入除第二原像攻击之外的其他攻击向量——详见文末的练习题。
结论
第二原像攻击之所以能够生效,是因为我们可以提供一个有效证明的缩短版本来重新生成 Merkle 根。Merkle 根确实有一个原像——然而,根节点是逐步生成的,而不是通过一次性哈希整个证明生成的。通过从序列中较晚的位置开始证明,我们可以使用一个能生成相同根节点的替代输入。
这种攻击很容易防御——只需不允许将非叶子节点的值解释为叶子节点即可。
练习题
以下 Capture the Flag 练习不正确地使用了 Merkle 证明,因此可以被黑客攻击:
RareSkills Riddles: Furry Fox Friends
通过 RareSkills 了解更多
请查看我们的 blockchain bootcamp 以了解我们的教育课程。
原载于 2023 年 11 月 24 日