本文介紹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十大安全問題 — 匯智網