以太坊作为全球领先的区块链平台,其智能合约技术为去中心化应用(DApps)的蓬勃发展奠定了基石,智能合约的代码一旦部署到区块链上,其便难以修改且透明可查,这使得合约的安全性至关重要,在众多智能合约安全威胁中,“重入”(Reentrancy)攻击因其造成的巨大破坏力和历史教训,成为了开发者必须高度警惕的“头号公敌”之一,本文将深入探讨以太坊智能合约中的重入攻击原理、典型案例以及有效的防御策略。
什么是重入攻击?
重入攻击,本质上是一种利用合约调用外部合约或发送以太币时的执行流程漏洞,使得攻击者能够恶意地“重新进入”(再次调用)合约的函数,从而非法执行操作或窃取资金的攻击方式。

在以太坊虚拟机(EVM)中,当一个合约调用另一个合约或发送以太币(通过.call()、.transfer()或.send())时,当前合约的执行会暂停,转而执行外部合约的代码,外部合约的执行完毕后,控制权会返回到原合约,从暂停的地方继续执行,重入攻击正是利用了这一“外部代码执行-返回”的窗口期。
重入攻击的经典案例:The DAO事件
提及重入攻击,就不得不提2016年震惊整个以太坊社区的“The DAO”事件,The DAO是一个基于以太坊的去中心化自治组织,旨在通过智能合约进行风险投资,其智能合约中存在重入漏洞。
攻击流程大致如下:
- 用户调用withdraw函数:The DAO的投资者调用合约的
withdraw函数,请求提取其投资份额对应的以太币。 - 内部转账与状态更新:合约首先将投资者的以太币转账到指定地址。
- 外部恶意合约的介入:攻击者构造了一个恶意合约,并在该恶意合约的回调函数(fallback函数)中再次调用了The DAO合约的
withdraw函数。 - 重入发生:由于The DAO合约在执行转账后,才更新投资者的“已提款”状态,攻击者的恶意合约在第一次收到转账后,其fallback函数被触发,再次调用
withdraw,合约检查发现该投资者的“已提款”状态尚未更新,以为这是第一次提款,于是再次进行转账。 - 循环与资金耗尽:这个循环过程不断重复,每次都能从The DAO合约中提取以太币,直到合约中的资金被洗劫一空,攻击者窃取了价值数千万美元的以太币,直接导致了以太坊的硬分叉,形成了现在的以太坊(ETH)和以太坊经典(ETC)。
The DAO事件不仅暴露了重入攻击的巨大危害,也推动了以太坊社区对智能合约安全性的深刻反思和改进。

重入攻击的常见模式与原理
重入攻击通常发生在合约函数中执行了以下操作时:
- 向外部的未知合约发送以太币(使用
.call()、.transfer()、.send())。 - 在发送以太币之前或同时,更新了合约的内部状态(如记录用户余额、标记已提款等)。
攻击者利用的正是状态更新的“时机”问题,如果状态更新发生在对外部调用之后,攻击者就可以在外部合约的回调函数中再次执行原函数,此时合约的状态尚未“反映”本次操作,从而绕过逻辑限制。
常见的重入模式包括:
- 单次重入:攻击者通过一次外部调用重入目标合约函数。
- 循环重入:攻击者通过恶意合约的循环调用,多次重入目标合约函数,直至耗尽目标合约的资金或达到其他恶意目的。
如何防御重入攻击?
幸运的是,开发者已经总结出了一系列行之有效的防御策略来抵御重入攻击,最核心、最被广泛推荐的就是检查-效果-交互(Checks-Effects-Interactions)模式。

-
检查-效果-交互(Checks-Effects-Interactions)模式:
- Checks(检查):在函数开头,首先进行所有必要的条件检查,例如检查调用者是否有权执行该操作、余额是否充足等。
- Effects(效果):在执行完所有检查后,立即更新合约的内部状态,减少用户的提款余额、标记订单为已处理等。这是防御重入的关键:状态更新必须在对外部交互之前完成。
- Interactions(交互):在所有状态更新完成之后,再进行与外部合约的交互,如调用其他合约函数或发送以太币。
遵循此模式,即使攻击者在交互阶段通过回调重入函数,由于状态已经更新,相关的检查(如余额是否足够)会失败,从而阻止了重入的继续。
-
使用内置的
.transfer()和.send()(在较新的Solidity版本中已不推荐,早期版本有一定防护): Solidity早期版本中,.transfer()和.send()会自动限制gas(2300 gas),这足以完成日志记录但不足以执行复杂的合约代码,从而在一定程度上阻止了重入,但这种方法并不可靠,因为外部恶意合约仍然可以进行一些操作,且现代Solidity版本更推荐使用.call()并配合gas限制。 -
使用Reentrancy Guard模式: 这是一个广泛使用的修饰器(Modifier),通过一个互斥锁(mutex)机制来防止函数在执行期间被重入,其基本原理是:
- 在函数调用时,先锁定状态(例如设置一个
locked变量为true)。 - 如果在函数执行期间再次调用该函数,由于
locked为true,调用会被直接拒绝。 - 函数执行完毕后,解锁状态(
locked设为false)。 OpenZeppelin等知名智能合约库提供了经过审计的ReentrancyGuard实现,开发者可以直接引入使用。
- 在函数调用时,先锁定状态(例如设置一个
-
避免在回调函数(fallback/receive)中执行复杂逻辑或修改状态: 回调函数是外部合约调用当前合约或向当前合约发送以太币时自动触发的函数,是重入攻击的主要入口点,应尽量保持回调函数的简洁性,避免在其中执行复杂的业务逻辑或修改关键状态。
重入攻击是以太坊智能合约安全领域中一个经典且极具破坏性的威胁,The DAO事件的历史教训警示我们,对合约安全性的丝毫松懈都可能导致灾难性后果。
作为智能合约开发者,必须深刻理解重入攻击的原理,并在开发过程中始终保持高度的安全意识,严格遵循“检查-效果-交互”模式,合理运用Reentrancy Guard等安全工具,对合约进行充分的测试和审计,是构建安全可靠以太坊智能合约的不二法门,唯有如此,以太坊生态的基石才能更加稳固,去中心化应用的潜力才能得到充分释放,安全,始终是区块链技术走向大规模应用的生命线。

