Rust智能合約DoS攻擊漏洞分析及解決方案

Rust智能合約中的拒絕服務攻擊

拒絕服務(DoS)攻擊可能導致智能合約在一段時間內甚至永久無法正常使用。主要原因包括:

  1. 合約邏輯存在缺陷,如某些函數的計算復雜度過高,導致Gas消耗超出限制。

  2. 跨合約調用中,合約執行依賴外部合約狀態,可能被外部合約阻塞。

  3. 人爲因素,如合約所有者丟失私鑰,導致特權函數無法調用。

下面通過具體例子分析智能合約中的DoS攻擊漏洞。

1. 循環遍歷可被外部修改的大型數據結構

以下是一個用於給註冊用戶分紅的簡單合約:

rust #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registered: Vec, pub accounts: UnorderedMap<accountid, balance="">, }

impl Contract { pub fn register_account(&mut self) { if self.accounts.insert(&env::predecessor_account_id(), &0).is_some() { env::panic("帳戶已註冊".to_string().as_bytes()); } else { self.registered.push(env::predecessor_account_id()); } log!("註冊帳戶 {}", env::predecessor_account_id()); }

pub fn distribute_token(&mut self, amount: u128) {
    assert_eq!(env::predecessor_account_id(), DISTRIBUTOR, "無權操作");
    for account in self.registered.iter() {
        let balance = self.accounts.get(&account).expect("獲取失敗");
        self.accounts.insert(&account, &balance.checked_add(amount).expect("加法溢出")); 
        log!("嘗試分發給帳戶 {}", &account);
        ext_ft_token::ft_transfer(
            account.clone(),
            amount,
            &FTTOKEN,
            0,
            GAS_FOR_SINGLE_CALL
        );
    }
}

}

該合約的問題在於registered數組大小沒有限制,可被惡意用戶操控變得過大,導致distribute_token函數Gas消耗過高。

推薦解決方案:

採用提現模式重構合約。讓用戶主動調用提現函數來獲取獎勵,合約只需記錄用戶可提現的獎勵數額。

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

考慮一個競價合約:

rust #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registered: Vec, pub bid_price: UnorderedMap<accountid,balance>, pub current_leader: AccountId, pub highest_bid: u128, pub refund: bool }

impl Contract { pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue { assert!(amount > self.highest_bid); if self.current_leader == DEFAULT_ACCOUNT { self.current_leader = sender_id; self.highest_bid = amount; } else { ext_ft_token::account_exist( self.current_leader.clone(), &FTTOKEN, 0, env::prepaid_gas() - GAS_FOR_SINGLE_CALL * 4, ).then(ext_self::account_resolve( sender_id, amount, &env::current_account_id(), 0, GAS_FOR_SINGLE_CALL * 3, )); } log!( "當前最高出價者: {} 最高出價: {}", self.current_leader, self.highest_bid ); PromiseOrValue::Value(0) }

#[private]
pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) {
    match env::promise_result(0) {
        PromiseResult::NotReady => unreachable!(),
        PromiseResult::Successful(_) => {
            ext_ft_token::ft_transfer(
                self.current_leader.clone(),
                self.highest_bid,
                &FTTOKEN,
                0,
                GAS_FOR_SINGLE_CALL * 2,
            );
            self.current_leader = sender_id;
            self.highest_bid = amount;
        }
        PromiseResult::Failed => {
            ext_ft_token::ft_transfer(
                sender_id.clone(),
                amount,
                &FTTOKEN,
                0,
                GAS_FOR_SINGLE_CALL * 2,
            );
            log!("退回當前出價");
        }
    };
}

}

該合約的問題在於:必須成功退回前一位出價者的代幣,才能更新最高出價。如果前一位出價者注銷了帳戶,合約將無法完成退款而阻塞。

解決方法:

考慮外部合約調用可能失敗的情況,實現合理的錯誤處理。可以將無法退回的代幣暫存在合約中,後續允許用戶主動提取。

3. 合約所有者私鑰丟失

部分合約函數被設置爲只有所有者可執行,用於更改關鍵系統變量。如果所有者私鑰丟失,這些函數將無法調用,可能導致合約無法正常運行。

解決方法:

設置多個合約所有者共同治理,或採用多籤名機制來替代單一所有者的權限控制,實現去中心化治理。

</accountid,balance></accountid,>

查看原文
此頁面可能包含第三方內容,僅供參考(非陳述或保證),不應被視為 Gate 認可其觀點表述,也不得被視為財務或專業建議。詳見聲明
  • 讚賞
  • 6
  • 分享
留言
0/400
PancakeFlippavip
· 1小時前
合约也有门槛咯 谁让你技术差
回復0
码农挖矿摆烂君vip
· 07-17 19:29
写代码的事下辈子再说8
回復0
NFT慈善家vip
· 07-17 19:28
其实…… *喝茶* 另一个可以优化以实现社会影响的低效合约。开发者何时才能学习到有意识的编码实践,真是无奈。
查看原文回復0
just_another_walletvip
· 07-17 19:20
安全问题老兄们必须重视啊
回復0
NFTragedyvip
· 07-17 19:16
这个问题啊,代码都没写完...
回復0
半佛薅羊毛vip
· 07-17 19:01
我擦 又要补坑 垃圾合约
回復0
交易,隨時隨地
qrCode
掃碼下載 Gate APP
社群列表
繁體中文
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)