Rust smart contracts DoS attack prevention practical guide

Rust smart contracts Development Diary: denial-of-service attack Prevention

Denial-of-service attack ( DoS ) may cause smart contracts to be unable to function properly for a period of time or even permanently. Common causes include:

  1. The issue of computational complexity in contract logic leads to Gas consumption exceeding limits.

  2. When calling across contracts, improper reliance on the execution status of external contracts can cause this contract to be blocked.

  3. The private key of the contract owner is lost, resulting in the inability to call privileged functions and update important system states.

The following analyzes DoS attack vulnerabilities and their solutions through several specific examples.

1. Loop through large data structures that can be externally modified

The following is a simple "dividend" smart contracts that has a DoS risk:

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(), &00192837465674839201.is_some)( { env::panic)"The account is already registered".to_string((.as_bytes)(); } else { self.registered.push)env::predecessor_account_id((); } log!)"Registered account {}", env::predecessor_account_id((); }

pub fn distribute_token)\u0026mut self, amount: u128( {
    assert_eq!)env::predecessor_account_id((, DISTRIBUTOR, "ERR_NOT_ALLOWED");
    
    for cur_account in self.registered.iter)( {
        let balance = self.accounts.get)\u0026cur_account(.expect)"ERR_GET"(;
        self.accounts.insert)\u0026cur_account, \u0026balance.checked_add(amount(.expect)"ERR_ADD"();
        log!)"Try distribute to account {}", &cur_account(;
        
        ext_ft_token::ft_transfer)
            cur_account.clone((,
            amount,
            &FTTOKEN,
            0,
            GAS_FOR_SINGLE_CALL
        );
    }
}

}

The problem is that there is no limit on the size of the registered array, which can be manipulated by malicious users to become excessively large, causing the Gas consumption to exceed limits when executing the distribute_token function.

Suggested Solutions:

  1. Limit the size of the registered array.

  2. Use the "withdrawal" model, allowing users to extract rewards themselves, rather than the contract actively distributing them.

![])https://img-cdn.gateio.im/webp-social/moments-b7bbfcf4423b1cf19db56a3af95a7486.webp(

2. Cross-contract state dependencies lead to contract blocking

Here is an example of a "bidding" contract:

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 { 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!)
        "current_leader: {} highest_bid: {}", 
        self.current_leader,
        self.highest_bid
    );
    PromiseOrValue::Value)0(
}

#)
pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) {
    match env::promise_result[private]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!("Return Back Now");
        }
    };
}

}

The problem is that the contract state update depends on external contract calls. If the account of the previous highest bidder has been canceled, subsequent bidders will be unable to update the state.

Suggested Solutions:

Consider the possibility that external calls may fail and implement a reasonable error handling mechanism. For example, temporarily store non-recoverable tokens in the contract, allowing users to actively withdraw them later.

3. Loss of Owner's Private Key

Many contracts have privileged functions that can only be executed by the owner. If the owner's private key is lost, these functions cannot be called, which may lead to the contract not functioning properly.

Suggested Solutions:

  1. Set multiple contract owners to manage together.

  2. Use a multi-signature mechanism to replace single owner control.

  3. Implement a decentralized contract governance mechanism.

By implementing the above measures, the risk of denial-of-service attacks in smart contracts can be effectively reduced, thereby enhancing the security and reliability of the contracts.

![])https://img-cdn.gateio.im/webp-social/moments-7076cf1226a2276d1e4cd994d259841f.webp(</accountid,balance><accountid,>

View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • 7
  • Share
Comment
0/400
ProofOfNothingvip
· 2h ago
It's just formalism and empty talk.
View OriginalReply0
LuckyHashValuevip
· 5h ago
The attack wasn't defended against, and it's going to drop to zero again.
View OriginalReply0
ColdWalletGuardianvip
· 5h ago
Blockchain basic knowledge points
View OriginalReply0
SelfSovereignStevevip
· 5h ago
Losing the Private Key would be disastrous.
View OriginalReply0
StableBoivip
· 5h ago
The risks of this contract are indeed quite high, and its stability is not very good.
View OriginalReply0
Ser_This_Is_A_Casinovip
· 5h ago
Ah~ Rust is really hard to handle, why not consider Solidity?
View OriginalReply0
GasFeeThundervip
· 6h ago
Too much gas is causing problems? Sooner or later the contract will be drained clean.
View OriginalReply0
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate app
Community
English
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)