Атаки типа «отказ в обслуживании» в смарт-контрактах Rust
Атака типа «отказ в обслуживании» (DoS) может сделать смарт-контракт непригодным для использования на определенный период времени. На это есть несколько причин:
В логике контракта есть дефект, заключающийся в том, что вычислительная сложность слишком высока, в результате чего расход газа превышает лимит.
При вызове между контрактами выполнение контракта зависит от ненадежного внешнего состояния контракта, что приводит к блокировке.
Владелец контракта потерял приватный ключ, что привело к невозможности выполнения ключевых привилегированных функций.
Ниже приведен анализ этих DoS-уязвимостей с конкретными примерами.
1. Обход больших структур данных, которые могут быть изменены извне
Ниже приведен простой «дивидендный» контракт с риском DoS:
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 или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
Практическое руководство по защите смарт-контрактов Rust от атак DoS
Атаки типа «отказ в обслуживании» в смарт-контрактах Rust
Атака типа «отказ в обслуживании» (DoS) может сделать смарт-контракт непригодным для использования на определенный период времени. На это есть несколько причин:
В логике контракта есть дефект, заключающийся в том, что вычислительная сложность слишком высока, в результате чего расход газа превышает лимит.
При вызове между контрактами выполнение контракта зависит от ненадежного внешнего состояния контракта, что приводит к блокировке.
Владелец контракта потерял приватный ключ, что привело к невозможности выполнения ключевых привилегированных функций.
Ниже приведен анализ этих 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");
}
Здесь self.registered может быть расширен до бесконечности, что приведет к нехватке газа во время обхода.
Следует использовать режим "вывод средств", чтобы пользователи могли активно извлекать награды:
ржавчина pub fn withdraw(&mut self) { let account_id = env::p redecessor_account_id(); let amount = self.accounts.get(&account_id).expect("Нет награды");
}
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);
}
#[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("Нет потерянных средств");
}
!
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,>