📢 早安!Gate 廣場|4/5 熱議:#假期持币指南
🌿 踏青還是盯盤?#假期持币指南 帶你過個“放鬆感”長假!
春光正好,你是選擇在山間深呼吸,還是在 K 線裡找時機?在這個清明假期,曬出你的持幣態度,做個精神飽滿的交易員!
🎁 分享生活/交易感悟,抽 5 位幸運兒瓜分 $1,000 仓位體驗券!
💬 茶餘飯後聊聊:
1️⃣ 假期心態: 你是“關掉通知、徹底失聯”派,還是“每 30 分鐘必刷行情”派?
2️⃣ 懶人秘籍: 假期不想盯盤?分享你的“掛機”策略(定投/網格/理財)。
3️⃣ 四月展望: 假期過後,你最看好哪個幣種“春暖花開”?
分享你的假期姿態 👉 https://www.gate.com/post
📅 4/4 15:00 - 4/6 18:00 (UTC+8)
我只是想分享一個許多開發者常忽略的智能合約安全問題——重入攻擊。如果你正在用 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。理解重入攻擊及其防範措施,能幫助你打造更安全的智能合約。