重入攻击:为什么智能合约不断被抽走 (以及如何阻止它)

快速版本:重入攻击就像一个黑客在你仍在向他们转账时给你回电——他们在交易完成之前就把你的钱包里的钱抽走了。

残酷的真相是:超过$100M 已经因重入攻击而损失。最著名的?DAO黑客(2016)通过利用这个确切的漏洞窃取了$50M 个ETH。

攻击是如何工作的 (使用真实代码逻辑)

想象一下,ContractA 持有 10 ETH,而 ContractB 内部存储有 1 ETH 的余额。

当 ContractB 调用 withdrawAll() 时,应该发生以下情况:

  1. 检查余额 > 0 ✓
  2. 发送ETH回去 ✓
  3. 更新余额至 0 ✓

但问题在于:攻击者利用了运算顺序

漏洞流程:

  • 攻击者调用 attack() → 对 ContractA 调用 withdrawAll()
  • ContractA 发送 1 ETH 并触发攻击者的 fallback() 函数
  • 在余额更新为0之前,fallback()会立即再次调用withdrawAll()
  • ContractA 检查: “余额 > 0?” 是的 (因为它还没有更新!)
  • 发送另一个 1 ETH → 再次触发 fallback()
  • 这个循环直到 ContractA 完全耗尽

关键见解:余额更新发生在 ETH 转账之后。这是脆弱性窗口。

三种防御策略

1. 非重入修饰符 (单一函数保护)

在执行时锁定功能。不允许重新进入: 合约 modifier nonReentrant { require(!locked, “No reentrancy”); 锁定 = true; _; 锁定 = false; }

简单,但只保护一个功能。

2. 检查-效果-交互模式 (多功能保护)

这就是游戏规则的改变者:

  • 检查: 验证条件 (balance > 0)
  • 效果:更新状态 (balance = 0) ← 在发送 ETH 之前移动此项
  • 交互: 发送 ETH

错误的订单:

require(balance > 0); → 发送 ETH →余额 = 0;太迟了!

正确的顺序:

require(balance > 0); → balance = 0; // 更新第一 → 发送 ETH // 然后互动

现在即使fallback()重新进入,余额已经为0。攻击失败。

3.GlobalReentrancyGuard (跨合约Protection)

对于具有多个相互作用合约的复杂系统,请使用一个集中式保护合约来跟踪所有合约的锁定状态。当合约A调用合约B时,保护合约记录该操作——如果合约B在返回之前试图回调系统,保护合约将阻止该操作。

为什么这很重要

重入攻击不仅仅是Solidity问题——它是一个设计问题。每当你发送ETH或调用外部函数时,你就将控制权交给了不受信任的代码。攻击者的回调函数在你的合约上下文中运行。

数据:Chainalysis发现2023-2024年约60%的高价值漏洞涉及重入或类似模式。像Yearn、Curve和Balancer这样的顶级项目都曾出现重入恐慌。

底线:默认使用检查-效果-交互模式。在需要的地方添加非重入锁。对于多合约系统,实施全局重入保护。超过1亿美元的损失本可以通过这些基本模式来挽回。

关注 @TheBlockChainer 获取更多 Web3 安全深入分析。

ETH3.86%
CRV3.57%
BAL3.18%
查看原文
此页面可能包含第三方内容,仅供参考(非陈述/保证),不应被视为 Gate 认可其观点表述,也不得被视为财务或专业建议。详见声明
  • 赞赏
  • 评论
  • 转发
  • 分享
评论
0/400
暂无评论
交易,随时随地
qrCode
扫码下载 Gate App
社群列表
简体中文
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)