古老的Solidity智能合約錯誤代碼編寫

        任何編程語言都有不完善的地方,而使用語言的過程中也可能產生一些邏輯上的Bug。在Solidity0.4.23版本的時候,有人在GitHub上列舉了一些使用Solidity編寫智能合約時常見的錯誤用法。雖然現在大家基本上都不會再寫同樣的問題代碼,但是重新學習一下仍然有着借鑑意義。

1、tx.origin

錯誤用法:判斷調用者地址時使用tx.origin作爲驗證地址。
原因:tx.orgin作爲交易的外部發起者,不管中間合約調用(消息)有多少次,它是固定不變的。因此如果tx.origin無意中調用了一個攻擊合約,攻擊合約再調用被攻擊合約,就能通過這個驗證。
修復:使用 msg.sender作驗證。

2、storage override

錯誤用法:向一個類似結構數組的狀態變量增加元素時,在創建元素時,錯誤的使用了storage 存儲區域。
原因:新創建storage存儲區域的元素未初始化時,它的指針指向了存儲位置0,內容會被改寫。
修復:函數內創建結構類型變量時使用memory關鍵字。

3、send

錯誤用法:使用send向一個地址發送ETH時未驗證返回值。
原因:如果send返回fail(比如一個合約拒收ETH),此時ether並沒有發送成功。
修復:使用transfer而不是send(transfer發送失敗會拋出錯誤引起重置)。

4、reentrancy(重入攻擊)

錯誤用法:在發送ETH之前未改變數據狀態。
原因:如果採用低級的call發送eth(或者有足夠的gas),在接收方爲合約時,可以執行一個回調函數來再次調用這個發送ETH的方法。
修復:將狀態改變寫在發送ETH之前,使重入無法通過條件驗證。

5、overunderflow

錯誤用法:直接使用運算符進行算術操作。
原因:數據類型都是有大小的,超過了會溢出。
修復:使用safeMath庫。

6、forceether

錯誤用法:當一個合約拒絕接收ETH時,使用this.balance == 0來作一個驗證條件。
原因:當一個合約自殺時,它可以指定一個地址來發送剩餘的ETH,這個ETH轉移無法被拒絕。
修復:不要依賴this.balance == 0作驗證條件

7、extcodesize

錯誤用法:使用它來檢查一個地址是否合約,即判斷它的代碼長度是否爲0。
原因:當合約A部署時,在構造器中可以調用外部合約 ,此時合約A的代碼長度爲0(未部署完成)。
修復:分兩種情況,如果只是限定一個方法不能爲合約調用,可以使用 tx.orgin != msg.sender作驗證,因爲如果是合約調用,它的tx.origin必定是一個外部賬號。如果只是驗證一個合約是合約,不作相反的認證,可以使用它的代碼長度不爲0來判斷。

8、dos

錯誤用法:當競標時,將上一個較低競標的ETH自動退回。
原因:當退回ETH給一個拒絕接收ETH的合約時,會退回失敗拋出錯誤,這樣就導致無法再提高競標價格。
修復:採用提取模式,用戶自提。

9、delegatecall

錯誤用法:比如含有delegatecall調用的合約初始化owner,只驗證了是不是委託調用的地址發過來的,而沒有驗證是否多次初始化。(這個有真實案例)
原因:delegatecall可以調用任何合約的任意方法,並且保持msg.sender不變,這一點尤其需要注意。ERC20代幣中的ApproveAndCall也存在一點點小問題。
修復:在使用delegatecall涉及到權限認證時,需要小心謹慎。

10、dos_unbound_array

錯誤用法:遍歷無固定大小的數組
原因:當數組很大很大時,可能會有問題(0.6.0對這一問題作了改進)
修復:使用分頁遍歷,每次只訪問固定數量的數組元素。

11、ArrayStorageOverride

錯誤用法:通過改變動態數組length來改變動態數組大小時,未驗證數組是否爲空。
原因:空數組再減1會溢出,形成一個巨大的數組,插入時會改寫其它存儲內容。
修復:增加數組長度大於0的驗證。(注:在Solidity0.6.0中已經不能通過修改動態數組長度來改變數組大小,數組長度爲只讀的,已經不存在這個問題了)


GITHUB地址 => https://github.com/fergarrui/ethereum-security

不對之處敬請大家留言指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章