重入攻擊:爲什麼智能合約不斷被抽走 (以及如何阻止它)

快速版本:重入攻擊就像一個黑客在你仍在向他們轉帳時給你回電——他們在交易完成之前就把你的錢包裏的錢抽走了。

殘酷的真相是:超過$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.5%
CRV3.15%
BAL3.82%
查看原文
此頁面可能包含第三方內容,僅供參考(非陳述或保證),不應被視為 Gate 認可其觀點表述,也不得被視為財務或專業建議。詳見聲明
  • 讚賞
  • 留言
  • 轉發
  • 分享
留言
0/400
暫無留言
交易,隨時隨地
qrCode
掃碼下載 Gate App
社群列表
繁體中文
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)