乙太坊智能合約安全清單
是否有工具、方法清單來幫助審查智能合約的安全性?
例如,對於您查看的每個功能
- 是否有重新進入的可能性以及它在重新進入時的表現
- 如果循環耗盡氣體,它的行為如何
- 如果達到堆棧限制,它將如何表現
… 等等。
如果沒有這樣的工具可用,是否有關於乙太坊智能合約安全性的所有已知故障模式的良好來源或列表?
這是針對可能的攻擊以及如何防範它們的社區 wiki(沒有聲譽)答案。隨時更新列表。如果您的合約函式具有匹配先決條件的特徵,請根據給定的建議仔細評估您的函式。
這是僅啟用這些攻擊的潛在攻擊或不當行為的列表。有關智能合約程式最佳實踐的其他資源,請參閱答案末尾的資源連結。
研究潛在的攻擊媒介和過去的攻擊歷史
不學習歷史的人注定要重蹈覆轍。 這是已知智能合約攻擊的一個很好的總結。
擁有多個開發人員
一位開發人員編寫程式碼,另一位開發人員對其進行審查。在發育過程中,擁有一組以上的眼球很重要。問題應該在開發期間通過公開討論而不是在審計中發現。
使用知名庫
不要試圖自己開發像 ERC-20 這樣的智能合約。相反,請使用提供現成和經過實戰考驗的智能合約的開源庫。當您從頭開始開發某些東西時,您很可能會犯錯誤。
黃金標準庫包括但不限於
有一個測試套件
嘗試使用自動化測試套件確保您的 Solidity 程式碼具有 100%的程式碼覆蓋率。這可以確保您的程式碼是可測試的。自動測試套件覆蓋並執行智能合約程式碼的每一行和分支至少一次。
Solidity 單元測試通常用 Python(Brownie、web3.py)或 JavaScript(Hardhat、Truffle)編寫。
測試將針對智能合約進行交易,並檢查交易狀態是否與交易後縮進一樣。(懸而未決,狀態總是作為契約的字母,但是在這種情況下,字母和縮進不匹配。)
測試正面案例和負面案例 - 即即使您知道它不會發生也不應該發生的事情。有時,當程式碼存在並更新時,一些新問題會漏掉,它們會被過去的測試發現——這稱為回歸測試。
在沒有測試的情況下進行審計並不是很有效率,因為測試應該始終是編寫健壯程式碼的第一道防線。
設置Github 持續集成,為每個送出的人執行所有測試。報告會自動儲存以備將來使用。這也有助於其他人複製測試環境並在以後執行它,因為通常由於包升級,測試執行工具本身開始失敗。
例子
正確使用函式可見性修飾符
內部函式被標記為這樣,只有適當的作者才能呼叫該函式。
請參閱Parity 錢包黑客解釋。
呼叫棧攻擊
同義詞:淺堆棧攻擊、堆棧攻擊
先決條件:函式使用
send()
或call()
呼叫:攻擊者通過呼叫棧為 1023 的合約來操縱跨合約呼叫棧 call() 失敗。
保護:始終檢查 send() 和 call() 的返回值。
someAddress.send()
更喜歡someAddress.call.value()
更多資訊
重入攻擊
同義詞: 比賽條件
先決條件:函式使用
send()
或call()
用於transferFrom()
乙太幣、ERC-20 代幣或send()
ERC-777 代幣。呼叫:不受信任的被呼叫合約呼叫相同的函式,使其處於意外狀態。這就是 TheDAO 被黑的方式。攻擊可以連結到多個函式(跨函式競爭條件)。
保護:使用重入防護裝置保護您的功能。在您的函式中使用Check-Effect-Interact操作順序,呼叫任何可能反映回智能合約的內容。
檢查-效果-互動
使用此模式可最大程度地減少潛在再入攻擊的損害。
- 首先檢查,執行類似的東西
require()
- 然後Effect,更新計數器,比如
balance[address] -= 10
- 最後,做任何與互動有關的事情,並將通過 、
send()
和其他人 在其他合約中執行程式碼。call()``transferFrom()
更多資訊
- https://github.com/ConsenSys/smart-contract-best-practices
- https://twitter.com/bneiluj/status/1251448415503908864
- 提現後修改儲存中的餘額,是否會立即發生重入攻擊?
意外拋出的 DoS
先決條件:函式使用
send()
orcall()
with throw following on fail呼叫:攻擊者操縱合約狀態使得
send()
總是失敗(例如退款)保護:更喜歡拉式支付系統
send()
更多資訊
經濟攻擊
先決條件:您的智能合約讀取價格數據並根據它進行交易
呼叫:仲裁機會和不安全的價格饋送本身可能不是漏洞,但仍會使使用者面臨本可以避免的資金損失。在經濟攻擊中,攻擊者利用機會與某人進行交易以獲取利潤,通常基於代幣價格的差異。
保護:不要依賴交易所給出的現貨價格或自動做市智能合約。所有價格,包括穩定幣價格,都受到操縱。
流行的自動化做市商和智能合約提供了安全的功能來計算不同情況下的價格。
更多資訊
惡意庫
前提條件:使用外部合約作為庫並通過系統資料庫獲取。
呼叫:通過合約系統資料庫呼叫另一個合約函式(參見
library
Solidity 中的關鍵字)。保護:確保沒有可以在未來版本中更換的動態元件。
整數溢出
先決條件:函式接受一個在數學中使用的 uint 參數
呼叫:發送非常大或非常負的整數導致總和計算溢出
保護:在進行數學運算時始終檢查值的順序。例如https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
更多資訊
整數除法向下舍入
先決條件:支付邏輯需要除法運算符 /
呼叫:程序員的錯誤
保護:請注意,除法總是四捨五入
環路長度和氣體操作
其他:為數組分配太小的 int
先決條件:儲存內的任何循環、複製數組或字元串。合約使用者可以增加循環長度的 for 循環。考慮投票場景循環。
呼叫:攻擊者增加數組長度或操縱塊氣體限制
保護:使用拉式支付系統。分佈
send()
在多個交易中並檢查msg.gas
限制。
- https://blog.ethereum.org/2016/06/10/smart-contract-security/
- https://github.com/ConsenSys/smart-contract-best-practices
- https://ethereum.stackexchange.com/a/7298/620
回退功能消耗超過 2300 gas 的限制
先決條件:具有 catch all function() { } 的 Solidity 合約以接收通用發送
呼叫:程序員的錯誤
保護:100% 的測試覆蓋率。確保您的備份功能保持在 2300 gas 以下。使用測試套件檢查函式的所有分支。不要在備份功能中儲存任何內容。不要在備份功能中呼叫合約或發送乙太幣。
更多資訊:
- https://blog.ethereum.org/2016/06/10/smart-contract-security/
- https://github.com/ConsenSys/smart-contract-best-practices
強制餘額更新
先決條件:函式讀取合約總餘額並有一些依賴於它的邏輯
呼叫:selfdestruct(contractaddress) 可以強制升級餘額
保護:不要相信 this.balance 會保持在給定的範圍內
更多的
礦工搶先
同義詞:事務排序依賴 (TOD)
先決條件:投標式市場,如 DAI 清算和拍賣
呼叫:攻擊者在區塊鏈中最終確定之前在記憶體池中看到交易。攻擊者有一個特權連接,比如一個礦池,可以首先廣播他的交易並覆蓋原來的恩人。
保護:預送出方案
更多的
- https://www.theblockcrypto.com/post/45750/exploring-defi-trading-strategies-arbitrage-in-defi
- https://github.com/ConsenSys/smart-contract-best-practices
靜態分析工具
用於檢查程式碼中常見錯誤的靜態分析工具,例如整數溢出。他們無法檢查程式碼的意圖,但他們會嘗試針對眾所周知的常見問題分析程式碼。
資源