✍️ Gate 廣場「創作者認證激勵計劃」進行中!
我們歡迎優質創作者積極創作,申請認證
贏取豪華代幣獎池、Gate 精美周邊、流量曝光等超過 $10,000+ 豐厚獎勵!
立即報名 👉 https://www.gate.com/questionnaire/7159
📕 認證申請步驟:
1️⃣ App 首頁底部進入【廣場】 → 點擊右上角頭像進入個人主頁
2️⃣ 點擊頭像右下角【申請認證】進入認證頁面,等待審核
讓優質內容被更多人看到,一起共建創作者社區!
活動詳情:https://www.gate.com/announcements/article/47889
我只是想分享一個許多開發者常忽略的智能合約安全問題——重入攻擊。如果你正在用 Solidity 開發智能合約,這是你絕對要了解清楚的。
最簡單的說法,重入攻擊發生在一個合約調用另一個合約時,而被調用的合約可以在仍在執行中時再次調用原本的合約。想像你有 ContractA,裡面存有 10 Ether,ContractB 向它發送 1 Ether。當 ContractB 提取資金時,ContractA 會檢查餘額是否大於 0,如果是,就會發送 Ether 回去。然而,如果 ContractB 有一個 fallback function((備用函數)),它可以在 ContractA 的提取函數尚未完成時再次調用 ContractA 的提取函數。結果是?ContractB 的餘額仍被記錄為 1 Ether,因此它會再獲得 1 Ether,然後重複這個過程,直到 ContractA 的資金耗盡。
這種攻擊方式是怎麼運作的?攻擊者需要兩個東西:一個 attack( 函數來啟動攻擊,以及 fallback function 來再次調用提取函數。fallback function 是一個特殊的外部函數,沒有名字、沒有參數,任何人都可以通過調用不存在的函數、傳送沒有資料的交易,或直接發送 Ether(不帶資料)來觸發它。
舉個具體的例子:EtherStore 合約有一個 deposit) 函數用來存款,以及一個 withdrawAll( 函數用來提取全部資金。問題在於 withdrawAll) 會先檢查餘額、發送 Ether,然後才將餘額設為 0。這就留下了重入攻擊的空隙。
那麼,怎麼防範呢?我會介紹三個方法。
第一,使用 noReentrant 修飾符。這個想法很簡單:在函數執行時鎖住合約。如果有人試圖再次調用該函數,會先通過鎖的檢查,但鎖只會在函數完全執行完畢後才解開。修飾符是一種特殊的函數,可以讓你在不重複寫整個邏輯的情況下,為其他函數加入條件。
第二,採用 Check-Effect-Interaction 模式。不要在檢查條件、轉帳後再更新狀態,而是先檢查條件,立即更新狀態(例如餘額設為 0),再進行外部交互。這樣即使發生重入,餘額已經更新為 0,攻擊者就無法再提取額外資金。
第三,如果你的專案涉及多個合約相互作用,則需要用到 GlobalReentrancyGuard。不是只鎖定單一函數,而是用一個存放在獨立合約中的狀態變數,來鎖定整個系統。每次任何合約的任何函數被調用時,都會檢查系統是否被鎖定。如果已鎖定,交易就會被拒絕。這在你有像 ScheduledTransfer 這樣的合約,向 AttackTransfer 發送資金時特別有用——GlobalReentrancyGuard 可以阻止整個重入攻擊鏈的發生。
這三種方法的優點是可以根據情況搭配使用。一個重要的函數就用 noReentrant;多個相關函數用 Check-Effect-Interaction;整個專案較為複雜則用 GlobalReentrancyGuard。理解重入攻擊及其防範措施,能幫助你打造更安全的智能合約。