Практическое руководство по защите смарт-контрактов Rust от атак DoS

Атаки типа «отказ в обслуживании» в смарт-контрактах Rust

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

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

  2. При вызове между контрактами выполнение контракта зависит от ненадежного внешнего состояния контракта, что приводит к блокировке.

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

Ниже приведен анализ этих DoS-уязвимостей с конкретными примерами.

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

Ниже приведен простой «дивидендный» контракт с риском DoS:

ржавчина #[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  
    );
}

}

Здесь self.registered может быть расширен до бесконечности, что приведет к нехватке газа во время обхода.

Следует использовать режим "вывод средств", чтобы пользователи могли активно извлекать награды:

ржавчина pub fn withdraw(&mut self) { let account_id = env::p redecessor_account_id(); let amount = self.accounts.get(&account_id).expect("Нет награды");

self.accounts.insert(&account_id, &0);

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

}

2. Зависимость состояния между смарт-контрактами приводит к блокировке

Вот такой контракт "Аукцион":

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

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::предоплаченный_газ() - GAS_FOR_SINGLE_CALL * 4,
    ).then(ext_self::account_resolve(
        sender_id,
        сумма,
        &env::current_account_id(),
        0,
        GAS_FOR_SINGLE_CALL*3,
    ));
}

бревно!(
    "текущий_лидер: {} наивысшая_ставка: {}", 
    self.current_leader,
    self.highest_bid
);

PromiseOrValue::Value(0)

}

#[private] pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { match env::p romise_result(0) { PromiseResult::NotReady => недостижимо!(), 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!("Вернись назад сейчас"); } }; }

Если аккаунт предыдущего участника торгов был уничтожен, новая ставка будет заблокирована.

Следует учитывать возможность неудачного внешнего вызова, можно временно хранить невозвратные токены для последующего извлечения пользователем:

ржавчина pub fn withdraw_lost_funds(&mut self) { let account_id = env::p redecessor_account_id(); let amount = self.lost_funds.get(&account_id).expect("Нет потерянных средств");

self.lost_funds.удалить(&account_id);

ext_ft_token::ft_transfer(
    account_id,
    количество
    &FTTOKEN,
    0,
    GAS_FOR_SINGLE_CALL
);

}

!

3. Приватный ключ владельца утерян

Некоторые ключевые функции могут вызываться только владельцем контракта. Если владелец потеряет приватный ключ, эти функции не смогут быть выполнены.

Для управления контрактами следует использовать схему с множественной подписью, чтобы избежать единой точки отказа:

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

pub fn submit_transaction(&mut self, transaction: Transaction) { assert!(self.owners.contains(&env::p redecessor_account_ id())); // Добавить сделку в список ожидающих подтверждения }

pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains(&env::p redecessor_account_ id())); // Увеличить количество подтверждений // Если количество подтверждений достигло требования, выполнить транзакцию }

С помощью вышеуказанных методов можно эффективно предотвратить риски атаки типа «отказ в обслуживании» в смарт-контрактах.

! </accountid,></accountid,>

Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • 7
  • Поделиться
комментарий
0/400
ConsensusDissentervip
· 07-19 04:59
ценные идеи值得收藏学习
Посмотреть ОригиналОтветить0
MEVHunterZhangvip
· 07-18 23:52
Не зря это опытный совет.
Посмотреть ОригиналОтветить0
PumpBeforeRugvip
· 07-18 15:28
Кто оплачивает комиссию за газ?
Посмотреть ОригиналОтветить0
GmGnSleepervip
· 07-17 00:43
Безопасность превыше всего
Посмотреть ОригиналОтветить0
ImpermanentPhilosophervip
· 07-17 00:41
Просто перезагрузите, и всё.
Посмотреть ОригиналОтветить0
GasFeePhobiavip
· 07-17 00:23
Проблема с газовыми сборами велика.
Посмотреть ОригиналОтветить0
AllInAlicevip
· 07-17 00:21
Оптимизация газовой промышленности важна
Посмотреть ОригиналОтветить0
  • Закрепить