重入攻击

定义

重入攻击是一种智能合约漏洞,恶意合约可以在原始函数执行完成之前反复调用易受攻击的合约——利用的是受害者合约的状态(例如余额账本)是在外部调用之后而非之前更新这一特性。最著名的重入攻击是2016年6月的“The DAO”事件,攻击者利用重入漏洞窃取了价值6000万美元的以太坊,导致以太坊进行了备受争议的硬分叉以追回资金,并最终形成了以太坊/以太坊经典分叉。重入攻击仍然是一种常见的漏洞:Curve Finance资金池(2023年7月,7300万美元)就曾因只读重入漏洞而被利用。现代Solidity最佳实践包括“检查-效果-交互”(CEI)模式和重入保护修饰符,以防止此类攻击。

 重入攻击机制

易受攻击的合约(伪代码):function withdraw(uint amount) { require(balances[msg.sender] >= amount); msg.sender.call{value: amount}(“”); // 外部调用,在状态更新之前 balances[msg.sender] -= amount; // 调用之后状态更新 }

攻击合约:receive() external payable { // 当受害者发送 ETH 时调用 if (address(victim).balance >= 1 ETH) { victim.withdraw(1 ETH); // 在余额更新前重新进入! } }

攻击序列:攻击者向受害者合约存入 1 个 ETH。攻击者调用 victim.withdraw(1 ETH)。受害者向攻击者发送 1 个 ETH(触发 receive())。攻击者的 receive() 再次调用 victim.withdraw(1 ETH) → 余额仍然显示 1 个 ETH(尚未更新!)。循环重复,直到受害者的余额耗尽。最终:victim.balances[attacker] = 0 – 为时已晚。

修复方案(检查-效果-交互模式):function withdraw(uint amount) { require(balances[msg.sender] >= amount); // 检查 balances[msg.sender] -= amount; // 效果(先更新!) msg.sender.call{value: amount}(“”); // 交互(最后调用) } → 状态在外部调用之前更新 → 重新进入不会改变任何内容 

 重大再入漏洞利用

事件

日期

金额

漏洞

DAO

2016 年 6 月

60万美元(3.6万个以太坊)

经典重入;触发以太坊分叉

SpankChain

2018年XNUMX月

$ 38K

支付通道重入

Uniswap/Lendf.me

四月2020

$ 25M

跨职能重入

曲线金融

2023 年 7 月

$ 73M

Vyper编译器重入保护漏洞

汉堡交换

2021 年 5 月

$ 7.2M

交叉合同再进入

 常见问题解答

问:什么是“只读重入”?

只读重入(Curve 2023 年黑客攻击中利用的漏洞)是指攻击者在状态转换期间重新进入查看/读取函数。传统的重入攻击利用写入函数来改变状态,而只读重入攻击则利用合约状态正在被修改但查看函数返回的是中间(错误)状态的窗口期。如果借贷协议在攻击者触发的状态转换期间读取 Curve 资金池的价格,则可能读取到被篡改的价格——这使得攻击者无需像传统重入攻击那样直接抽取资金,就能实现经济上的剥削。

问:OpenZeppelin 的 ReentrancyGuard 能否消除重入风险?

OpenZeppelin 的 ReentrancyGuard 修饰符添加了一个互斥锁,可以防止在单个事务中发生可重入调用。对于大多数函数而言,这可以有效地防止经典的重入攻击。然而,它无法防止跨函数重入(调用同一合约中的不同函数)、跨合约重入(调用同一协议系统中的不同合约)或只读重入(视图函数)。多层防御——CEI 模式 + ReentrancyGuard + 审计 + 形式化验证——提供的保护比任何单一措施都更强大。

问:为什么 DAO 黑客事件导致了以太坊经典(Ethereum Classic)的出现?

在2016年6月的The DAO黑客事件之后,以太坊社区就是否进行硬分叉以追回被盗资金展开了激烈的辩论——这一备受争议的“救助”方案违背了区块链交易不可篡改的原则。社区的大多数成员投票决定进行硬分叉(由此创建了如今的以太坊,并追回了资金)。而少数坚持绝对不可篡改原则的成员则拒绝升级——他们的链成为了以太坊经典(ETC)。这场争议凸显了“代码即法律”的区块链系统中实际存在的治理难题。

UPay 小贴士:在使用较新的DeFi 协议时,一个快速的安全检查方法是查看 Etherscan 上的合约,寻找 OpenZeppelin 的 ReentrancyGuard 修饰符(在函数签名中查找“nonReentrant”),并检查代码是否遵循 Checks-Effects-Interactions 模式(状态更新发生在外部调用之前)。您无需成为安全专家——只需识别这些模式及其缺失即可发出重要的风险信号。实施多种重入防御模式的协议表明其具有安全意识;而未实施任何防御模式的协议可能尚未考虑到此类漏洞。

免责声明:本内容仅供教育用途,不构成财务建议。

UPay——让加密货币成为百科全书

新闻