Безопасность смарт-контрактов на Rust: 3 ключевые стратегии защиты от DoS-атак

Безопасность смарт-контрактов на Rust: защита от атаки типа «отказ в обслуживании»

атака типа «отказ в обслуживании» ( DoS ) может привести к тому, что смарт-контракты не могут нормально использоваться в течение некоторого времени, а даже к их постоянной недоступности. Основные причины включают:

  1. Логика контракта имеет дефект, связанный с чрезмерной вычислительной сложностью, что приводит к превышению лимита потребления газа.

  2. Зависимость от состояния выполнения внешнего контракта при вызове межконтрактов может быть заблокирована внешним контрактом.

  3. Владелец контракта потерял закрытый ключ и не может выполнить ключевые функции привилегий.

Ниже будет проведен анализ на конкретных примерах.

1. Избегайте обхода крупных структур данных, которые могут быть контролируемы извне

Следующий пример простого "смарт-контракта" на "выплату дивидендов" имеет риск атаки типа «отказ в обслуживании»:

ржавчина #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub зарегистрированные: Vec, аккаунты пабов: UnorderedMap<accountid, balance="">, }

pub fn register_account(&mut self) { если self.accounts.insert(&env::p redecessor_account_id(), &0).is_ some() { env::panic("Учетная запись уже зарегистрирована".to_string().as_bytes()); } иначе { self.registered.push(env::p redecessor_account_id()); } log!("Зарегистрированный аккаунт {}", env::predecessor_account_id()); }

pub fn distribute_token(&mut self, сумма: u128) { assert_eq!(env::p redecessor_account_id(), ДИСТРИБЬЮТОР, "ERR_NOT_ALLOWED");

для cur_account в self.registered.iter() {
    let balance = self.accounts.get(&cur_account).expect("ERR_GET");
    self.accounts.insert(&cur_account, &balance.checked_add(amount).expect("ERR_ADD" ));
    log!("Попробуйте распределить на аккаунт {}", &cur_account);
    
    ext_ft_token::ft_transfer(
        cur_account.клон(),
        сумма,
        &FTTOKEN,
        0,
        GAS_FOR_SINGLE_CALL
    );
}

}

Проблема с этим смарт-контрактом заключается в том, что размер массива registered не ограничен, что позволяет злоумышленникам манипулировать им, увеличивая его до больших размеров, что приводит к превышению расхода газа функции distribute_token.

Рекомендуемое решение – использовать режим "вывода".

ржавчина pub fn distribute_token(&mut self, сумма: u128) { assert_eq!(env::p redecessor_account_id(), ДИСТРИБЬЮТОР, "ERR_NOT_ALLOWED");

для аккаунта в self.registered.iter() {
    let balance = self.accounts.get(&account).unwrap(928374656574839201;
    self.accounts.insert)&account, &(balance + amount();
}

}

pub fn withdraw)&mut self( { let account = env::p redecessor_account_id)(; let amount = self.accounts.get)&account(.unwrap)928374656574839201; self.accounts.insert(&account, &0);

ext_ft_token::ft_transfer(account, сумма, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL);

}

!

2. Избегайте зависимости состояния при вызовах между смарт-контрактами

下面是一个"竞价" смарт-контракт示例:

ржавчина #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Контракт { паб зарегистрирован: Vec, паб bid_price: UnorderedMap<accountid,balance>, pub current_leader: AccountId, highest_bid паб: U128, }

pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue { assert!(amount > self.highest_bid);

если self.current_leader == DEFAULT_ACCOUNT {
    self.current_leader = sender_id;
    self.highest_bid = сумма;
} иначе {
    ext_ft_token::account_exist(
        self.current_leader.клон)(,
        &FTTOKEN,
        0,
        env::предоплаченный_газ() - ГАЗ_ДЛЯ_ОДИНОЧНОГО_ВЫЗОВА * 4,
    (.then)ext_self::account_resolve)
        sender_id,
        сумма,
        &env::current_account_id((,
        0,
        GAS_FOR_SINGLE_CALL*3,
    ();
}

PromiseOrValue::Value)0)

}

#( pub fn account_resolve)&mut self, sender_id: AccountId, amount: u128[private] { match env::p romise_result(0) { PromiseResult::Successful(_) => { ext_ft_token::ft_transfer( self.current_leader.клон)(, self.highest_bid, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, (; self.current_leader = sender_id; self.highest_bid = сумма; } PromiseResult::Failed => { ext_ft_token::ft_transfer) sender_id.клон)(, сумма, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, (; log!)"Вернуться сейчас"); } PromiseResult::NotReady => недостижимо!(), }; }

Проблема с этим смарт-контрактом заключается в том, что если предыдущий участник с наивысшей ставкой аннулирует свой аккаунт, новый участник торгов не сможет успешно обновить статус.

Решением является реализация разумной обработки ошибок, например, временное хранение невозвратных токенов, которые затем могут быть извлечены пользователем самостоятельно:

ржавчина pub struct Contract { // ... pub lost_found: UnorderedMap<accountid, balance="">, }

pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { совпадение env::promise_result(0) { PromiseResult::Successful(_) => { // Нормальная логика возврата } PromiseResult::Failed => { // Временное хранение невозвратных токенов let lost_amount = self.lost_found.get(&self.current_leader).unwrap_or(0); self.lost_found.insert(&self.current_leader, &)lost_amount + self.highest_bid((;

        // Обновить статус
        self.current_leader = sender_id;
        self.highest_bid = сумма;
    }
    PromiseResult::NotReady => недостижимо!)),
}

}

pub fn withdraw_lost_found(&mut self) { let account = env::p redecessor_account_id(); let amount = self.lost_found.get(&account).unwrap_or(0); если сумма > 0 { self.lost_found.insert(&account, &0); ext_ft_token::ft_transfer(account, сумма, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL); } }

3. Избегайте единой точки отказа

Потеря приватного ключа владельца контракта может привести к невозможности использования ключевых привилегий. Решение заключается в использовании механизма многофакторной подписи:

ржавчина pub struct Contract { владельцы пабов: Vec\u003caccountid\u003e, pub требуемые_подтверждения: u64, сделки в пабах: Vec, }

pub fn propose_transaction(&mut self, транзакция: Transaction) { assert!(self.owners.contains)&env::p redecessor_account_id(((, "Не ) владельца; self.transactions.push)транзакция); }

pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains)&env::p redecessor_account_id(((, "Не ) владельца;

let transaction = &mut self.transactions[transaction_id as usize];
транзакции.подтверждения += 1;

if transaction.confirmations >= self.required_confirmations {
    self.execute_transaction)transaction_id);
}

}

fn execute_transaction(&mut self, transaction_id: u64) { let transaction = self.transactions.remove(transaction_id как usize); // Выполнение логики транзакции }

С помощью механизма мультиподписей можно избежать паралича контракта из-за утраты закрытого ключа единственным владельцем, повысив степень децентрализации и безопасность контракта.

! </accountid,></accountid,balance></accountid,>

Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • 6
  • Поделиться
комментарий
0/400
TokenomicsTherapistvip
· 20ч назад
Потерять закрытый ключ — это слишком реально, ха-ха.
Посмотреть ОригиналОтветить0
GasFeeNightmarevip
· 08-04 04:49
Не зря это Крупные инвесторы по Gas
Посмотреть ОригиналОтветить0
PretendingToReadDocsvip
· 08-04 04:49
DoS не избежать, о какой безопасности можно говорить?
Посмотреть ОригиналОтветить0
HashRatePhilosophervip
· 08-04 04:32
Рота Rust пришла, пусть противники дрожат!
Посмотреть ОригиналОтветить0
TestnetFreeloadervip
· 08-04 04:28
防dos你搁这 ловушка理论呢
Посмотреть ОригиналОтветить0
MevShadowrangervip
· 08-04 04:26
Газовые сборы зашкаливают, я в шоке.
Посмотреть ОригиналОтветить0
  • Закрепить