深入了解重入攻击及有效防护方法

重入攻击是智能合约中最严重的安全威胁之一。本文将不仅介绍重入攻击的工作原理,还将全面指导你如何保护你的项目。

为了全面理解,我们将从基础概念开始,分析实际攻击的源代码,最后探索三种经过验证的防御技术:从 modifier nonReentrant() 到 GlobalReentrancyGuard() 以及“检查-效果-交互”模式。

什么是重入攻击?

想象两个智能合约相互交互。ContractA 和 ContractB 完全可以相互调用。这看似正常,但实际上这是攻击者可以利用的漏洞。

重入的核心概念是:一个智能合约可以在另一个合约仍在执行时,反向调用该合约。这会形成无限循环,除非被正确控制。

举例说明:ContractA 持有 10 Ether,其中 ContractB 已经向其发送了 1 Ether。正常情况下,当 ContractB请求提款时,会检查(余额 > 0),然后转账 Ether,最后将余额设为 0。但在重入攻击中,这个过程可以在余额更新之前反复执行多次。

重入攻击的工作机制

攻击者需要两个主要部分:一个 attack() 函数和一个 fallback() 函数。

fallback 是 Solidity 中的特殊函数——没有名字、没有参数、没有返回值。当满足以下条件时会自动触发:

  • 调用不存在的函数
  • 向合约发送没有指定函数的数据
  • 向合约转账 Ether 时没有附带数据

攻击流程如下: 步骤1: 攻击者调用 attack(),在其中调用 ContractA 的提款函数。

步骤2: ContractA 检查余额(大于0),然后转账 1 Ether 给 ContractB,并触发 fallback。

步骤3: 关键点——余额尚未更新,仍为原值。

步骤4: fallback 被触发,立即反向调用 ContractA 的提款函数。

步骤5: ContractA 再次检查余额(仍大于0),再次转账 1 Ether,触发 fallback。

此过程不断重复,直到 ContractA 的 Ether 被全部转出。这就是重入攻击的危险所在。

具体攻击代码分析

以 EtherStore 合约为例,它有两个主要函数:

  • deposit():存款并更新余额
  • withdrawAll():一次性提取全部余额

withdrawAll() 的流程:检查余额(>0)、转账 Ether、将余额设为0。

漏洞在于:转账发生在余额更新之前,攻击者可以在转账过程中反复调用。

攻击合约 Attack 设计如下:

  • 构造函数接受 EtherStore 地址
  • fallback() 会在 Ether 转账时反复调用 withdrawAll()
  • attack() 开始攻击流程

攻击步骤:

  1. 攻击者调用 attack(),存入 Ether
  2. attack() 调用 EtherStore 的 withdrawAll()
  3. EtherStore 转账 1 Ether,触发 fallback
  4. fallback 调用 withdrawAll(),余额未更新,继续转账
  5. 循环直到 Ether 被全部转出

三种防御重入攻击的技术

技术一:使用 modifier nonReentrant

最简单的保护单个函数的方法是用 nonReentrant 修饰符。它在函数开始时锁定合约,防止在执行过程中再次调用。

工作原理:

  • 在函数执行前锁定
  • 结束后解锁
  • 任何重入调用都会被拒绝

缺点:

  • 只保护单个函数,不防止不同函数间的重入

技术二:检查-效果-交互(Check-Effect-Interaction)

更精细的防护方式。通过调整代码执行顺序:

  • 检查(Check): 先验证条件
  • 效果(Effect): 立即更新状态
  • 交互(Interaction): 最后调用外部合约或转账

示例:

  • 不安全:先转账,再更新余额
  • 安全:先更新余额,再转账

这样即使发生重入,余额已被更新,不会被重复利用。

技术三:GlobalReentrancyGuard(全局重入保护)

适用于多个合约交互复杂的项目。创建一个中心控制的合约,用于全局检测重入。

原理:

  • 所有合约都引用同一个重入锁
  • 防止跨合约的链式重入攻击

例如:在 ScheduledTransfer 和 AttackTransfer 之间的交互中,GlobalReentrancyGuard 能有效阻止攻击。

选择合适的防御技术

使用 nonReentrant:

  • 只需保护少数函数
  • 适合简单场景
  • 费用较低

使用 Check-Effect-Interaction:

  • 适合多函数保护
  • 更节省 gas
  • 推荐最佳实践

使用 GlobalReentrancyGuard:

  • 多合约系统
  • 复杂交互
  • 需要集中管理

结合使用这些技术,依据项目具体需求灵活选择。

结论

重入攻击并非不可防范。只要理解其机制,合理应用防御技术,就能有效保护智能合约。从简单的 modifier 到全面的全局保护,每种工具都在安全体系中扮演重要角色。

安全不是选择,而是责任。理解重入攻击,采取适当措施,你就能构建既安全又高效的智能合约。

关注 @TheBlockChainer,获取更多Web3安全、Solidity、智能合约审计等相关内容。

查看原文
此页面可能包含第三方内容,仅供参考(非陈述/保证),不应被视为 Gate 认可其观点表述,也不得被视为财务或专业建议。详见声明
  • 赞赏
  • 评论
  • 转发
  • 分享
评论
请输入评论内容
请输入评论内容
暂无评论