Rust智能合約養成日記(6)
BlockSec
2022-03-29 10:35
本文约2976字,阅读全文需要约12分钟
拒絕服務攻擊又稱DoS (Denial of Service)攻擊,該類型的攻擊將使得智能合約在一段時間內(甚至永久)無法被用戶正常使用。

相關文章:

拒絕服務攻擊又稱DoS (Denial of Service)攻擊,該類型的攻擊將使得智能合約在一段時間內(甚至永久)無法被用戶正常使用。

目前已知的原因大致可分為如下兩類:

  • 合約邏輯中存在的某些缺陷。如某一public函數,其實現沒有考慮到計算複雜度。用戶調用該函數時,實際所需消耗的Gas會超出NEAR公鏈創世區塊配置文件(genesis_config.json)中所定義的"max_total_prepaid_gas": 300000000000000` (300TGas),導致交易失敗。

  • 某些跨合約調用情形中,合約的執行依賴於其他外部合約的執行狀態。而外部合約的執行並非總是可靠,以至於本合約的執行可能被外部合約阻塞,無法照常運行。該類問題的發生可表現為合約用戶在合約中的資金被鎖定,以至於無法正常的充值或提現。

  • 二級標題


二級標題

1. 循環遍歷一個可被外部調用更改的數據結構

以下是一個用於給合約中註冊用戶“分紅”的簡單智能合約,其狀態數據如下:

用戶可通過調用pub fn register_account()函數進行註冊並初始化。

後續該合約的管理者將調用pub fn distribute_token函數來為系統中用戶進行"分紅"。 “分紅”的方式為遍歷用戶數組self.registered,並通過跨合約調用向每一個用戶轉入指定額度amount的代幣以做獎勵。

然而該合約狀態數據(self.registered)的大小沒有限制,並且可以被惡意用戶所操控,使得該合約數據的大小變得過大。以至於DISTRIBUTOR用戶在調用該合約方法時,可能消耗的Gas費用過高,超出了GAS LIMIT。

如下是該合約在實際NEAR Localnet中測試的結果

可以看到當系統中註冊的用戶較多時,實際在distribute_token執行的過程中,所設置的prepaid_gas將不足以滿足所有用戶的轉賬操作,以至於本次交易失敗。

推薦的解決方案:

二級標題

二級標題

2. 跨合約之間的狀態依賴導致合約阻塞

合約在進行跨合約調用時,可能會對外部合約的狀態存在依賴,不恰當的依賴,會導致該合約阻塞,從而可能被發起DoS攻擊

下面考慮一種利用智能合約進行“競價”的場景:

用戶可以通過調用“競價合約”中的pub fn register_account函數方法註冊賬戶,為參與後續的競價做準備

用戶還可以通過如下接口函數查詢當前系統中目前為止出價最高的用戶ID,及其所出的價格。

用戶還可以通過如下接口函數查詢當前系統中目前為止出價最高的用戶ID,及其所出的價格。

當競價合約收到token時,會通過ft_on_transfer函數調用到如下bid函數。

在該出價函數中,函數的執行邏輯將首先檢查本次用戶的出價是否高於之前出價最高用戶的出價值。如果滿足該條件,將執行self.refund_exe()從“競價合約”中退回之前出價最高用戶的出價代幣。隨後更新目前為止出價最高的用戶ID及其所出的價格。

實際的情況是,根據該合約的邏輯定義:必須要退回之前出價最高用戶的出價代幣,才能將目前為止出價最高的用戶ID進行更替。

在該出價函數中,函數的執行邏輯將首先檢查本次用戶的出價是否高於之前出價最高用戶的出價值。如果滿足該條件,將執行self.refund_exe()從“競價合約”中退回之前出價最高用戶的出價代幣。隨後更新目前為止出價最高的用戶ID及其所出的價格。

實際的情況是,根據該合約的邏輯定義:必須要退回之前出價最高用戶的出價代幣,才能將目前為止出價最高的用戶ID進行更替。

此時測試模擬了“競價系統”的參與的用戶:user0、user1和user2

他們分別擁有10000個初始代幣。 user0首先在“競價系統”中出價1000,此時查詢可知current_leader: user0.test.near highest_bid: 1000。隨後user0立即將剩餘的9000個代幣轉給了user2,並銷毀了代幣賬戶。

此後,當user1出價2000時,系統將打算退回user0之前的出價值。但由於此時user0的賬戶已不存在,系統將提示"Cannot Refund",始終無法成功完成後續的交易更新狀態。

此時第二位出價者想出價2000:

  • 二級標題

二級標題

3. Owner私鑰丟失

去中性化智能合約項目中往往也存在部分中心化的現象:如存在合約的owner。部分合約函數的執行被設置為僅owner可以執行,用以對合約中某些關鍵系統變量值的進行設置更改。我們可以將此類函數稱之為only_owner類型函數。

例如前文在“分紅”合約中所定義的pub fn distribute_token, 該函數即為only_owner函數。當合約的owner無法履行職能(私鑰丟失)時,資金將一直被鎖定在合約之中,無法分發給其他用戶。另有大多數的情況下,only_owner函數還可以用來暫停或者重啟合約中的所有交易,可見owner正常履行其職能的重要性。

  • 解決方法:

為避免上述owner個人“失能”情形的發生,我們可增設多位合約的owner共同治理合約,甚至可採用多簽請求的方式來替換原有的合約權限控制方案,以此實現合約的去中心化治理效果。有關智能合約中多簽請求功能的設計實現,將在後續的《智能合約養成日記》中展開詳細的描述。

BlockSec
作者文库