資安

Solidity合約開發安全問題【TOP 10】

本文介紹CheckMarx安全研究小組通過掃描公開的以太坊智能合約所發現的Solidity智能合約開發中常見的十大安全問題,其中__未檢查的外部調用__ 和 高成本循環 分列排行榜前兩名。該安全問題排行榜於2020年5月發佈。

在2018年,CheckMarx安全研究小組首次發佈了Solidity智能合約開發中常見的十大安全問題排行榜,其中位居榜首的兩個問題分別是__外部合約拒絕服務__ 和 重入 。2020年的情況已經有所變化,

下表比較了2018年和2020年Solidity十大常見安全問題之間的變化。這些問題按嚴重程度和流行程度排序:

在這裡插入圖片描述

用自己熟悉的語言學習 以太坊DApp開發Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart

S1 – 未檢查的外部調用

這是我們之前的Solidity十大安全問題榜單上第三常見的問題。由於現在解決了前兩個問題,因此 未檢查的外部調用 已升至2020年更新列表中最常見的問題。

Solidity低級調用方法,例如address.call(),不會引發異常。相反,如果調用 遇到異常,則它們返回false。另一方面,如果 doSomething()拋出異常,則合約調用(例如 ExternalContract.doSomething() )會自動傳播異常。

使用addr.send()發送以太幣是一個很好的例子,應該通過檢查返回值來顯式處理不成功的發送,但這對於其他外部調用也有效:

在這裡插入圖片描述

S2 – 高成本循環

代價高昂的循環從Solidity安全榜單的第四名上升至第二名。儘管我們以前的清單中的前2個問題都已解決,但受該問題影響的智能合約數量卻增長了近30%。

以太坊環境的算力是需要付費的(使用以太幣)。因此,減少完成操作所需的計算步驟不僅是優化的問題,而且還涉及成本效率。

循環是昂貴操作的一個很好的例子:由於數組中包含許多元素,因此需要更多迭代才能完成循環。如你所料,無限循環會耗盡所有可用GAS。
在這裡插入圖片描述

如果攻擊者能夠影響元素數組的長度,則上述代碼將導致拒絕服務,從而阻止執行跳出循環。儘管它與十大常見問題相去甚遠,但在掃描的智能合約中發現有8%的合約存在數組長度操縱問題。

S3 – 權力過大的所有者

這是Soldiity十大安全問題榜單中的一個新條目,該問題影響了約16%的已掃描智能合約。

某些合約與其所有者(Owner)緊密相關,使得某些功能只能由所有者地址調用,如下例所示:

在這裡插入圖片描述

只有合約所有者能夠調用doSomething()doSomethingElse()函數:前者使用onlyOwner修飾符,而後者則顯式執行該修飾符。這帶來了嚴重的風險:如果所有者的私鑰遭到洩露,則攻擊者可以控制該合約。

S4 – 算術精度問題

由於使用256位虛擬機(EVM),因此Solidity的數據類型有些複雜。該語言不提供浮點表示形式,並且少於32個字節的數據類型將被打包到同一個32字節的槽位中。考慮到這一點,你應該會想到可能存在的精度問題:

在這裡插入圖片描述

如上例所示,在乘法之前執行除法時,可以預料存在很大的舍入誤差。

S5 – 過於依賴tx.origin

智能合約不應依賴於tx.origin進行身份驗證,因為惡意合約可能會進行中間人攻擊,耗盡所有資金。
建議改用msg.sender

在這裡插入圖片描述
可以在Solidity的文檔中查看Tx Origin攻擊 的詳細說明。簡單的說,tx.origin始終是合約調用鏈中的第一個帳戶,而msg.sender則表示直接調用者。如果鏈中的最後一個合約依賴於tx.origin進行身份驗證,那麼調用鏈中間環節的合約將能夠榨乾被調用合約的資金,因為身份驗證沒有檢查究竟是誰(msg.sender)進行了調用。

S6 – 上溢出/下溢出

Solidity的256位虛擬機(EVM)存在上溢出和下溢出問題,具體演示可以查看這裡。在for循環條件中使用uint數據類型時,開發人員要格外小心,因為它可能導致無限循環:

在這裡插入圖片描述

在上面的示例中,當i的值為0時,下一個值為2^256 -1,這使條件始終為true。開發人員應當儘量使用<>!===進行比較。

S7 –不安全的類型推斷

該問題在Solidity十大安全問題排行榜中上升了兩個位置,現在影響到的智能合約比之前多出17%以上。

Solidity支持類型推斷,但有一些奇怪的表現。例如,字面量0會被推斷為byte類型,而不是我們通常期望的整數類型。

在下面的示例中,i的類型被推斷為uint8,因為這時能夠存儲i的值的最小長度的整數類型。但如果elements數組包含256個以上的元素,則下面的代碼就會發生溢出:

在這裡插入圖片描述
因此,我們建議顯式聲明數據類型,以避免代碼意外的行為或錯誤。

S8 – 不正確的轉賬

此問題在Solidity十大安全問題榜單中從第六位下降到第八位,目前影響不到1%的智能合約。

在合約之間進行以太幣轉賬有多種方法。雖然官方推薦使用addr.transfer(x)函數,但我們仍然找到了還在使用send()函數的智能合約:

在這裡插入圖片描述

請注意,如果轉賬不成功,則addr.transfer(x)會自動引發異常,這樣可以減輕了先前討論的未檢查外部調用的問題S1。

S9 –循環內轉帳

當在循環中進行以太幣轉賬時,如果其中一個合約不能收到,那麼整個交易將被回滾。

在這裡插入圖片描述

攻擊者可能利用此行為來導致拒絕服務,從而阻止其他合約接收以太幣。

S10 – 時間戳依賴性

在Soldity十大安全問題榜單中,該問題在2018年排名第五。

重要的是要記住,智能合約在不同時刻在多個節點上運行。以太坊虛擬機(EVM)不提供時鐘時間,並且通常用於獲取時間戳的now變量實際上是礦工可以操縱的環境變量(block.timestamp的別名)。

在這裡插入圖片描述

由於礦工當前可以操縱環境變量,因此只能在不等式><>=<=中使用其值。

如果你的應用需要隨機性,可以參考RANDAO合約,該合約基於任何人都可以參與的去中心化自治組織(DAO),是所有參與者共同生成的隨機數。

總結

在比較2018年和2020年的Solidity十大常見安全問題列表時,我們可以觀察到有關開發最佳實踐的一些進展,尤其是那些影響安全性的實踐。看到2018年排名前2的問題,__外部合約拒絕服務__ 和 重入 離開前十名榜單是一個積極信號,但開發者仍然需要採取一些重要步驟來避免常見錯誤。


原文鏈接:Solidity十大安全問題 — 匯智網

Leave a Reply

Your email address will not be published. Required fields are marked *