Безпека смартконтрактів Rust: захист від атаки на відмову в обслуговуванні
атака на відмову в обслуговуванні(DoS) може призвести до того, що смартконтракти не зможуть нормально використовуватися протягом певного часу, а іноді навіть до їхнього постійного виходу з ладу. Основні причини включають:
Логіка контракту має дефект надмірної обчислювальної складності, що призводить до перевищення обмеження споживання Gas.
Залежність від стану виконання зовнішнього смартконтракту під час міжконтрактних викликів може бути заблокована зовнішнім контрактом.
Власник контракту втратив приватний ключ, не може виконати ключові функції привілеїв.
Нижче наведено аналіз з конкретними прикладами.
1. Уникайте обходу великих структур даних, які можуть бути керовані ззовні
Наступний є простим "бонусним" смартконтрактом, що містить ризик атаки на відмову в обслуговуванні:
ржавчина
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
публічна структура Контракт {
pub зареєстровано: Vec,
облікові записи pub: 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());
} else {
self.registered.push(env::p redecessor_account_id());
}
log!("Зареєстрований акаунт {}", env::predecessor_account_id());
}
для cur_account в self.registered.iter() {
let balance = self.accounts.get(&cur_account).expect("ERR_GET");
self.accounts.insert019283746574839201&cur_account, &balance.checked_add(amount(.expect)"ERR_ADD" ();
log!)"Спробуйте розподілити на рахунок {}", &cur_account(;
ext_ft_token::ft_transfer)
cur_account.clone((,
сума,
&FTTOKEN,
0,
GAS_FOR_SINGLE_CALL
);
}
}
Проблема цього смартконтракту полягає в тому, що розмір масиву registered не має обмеження, що дозволяє зловмисним користувачам маніпулювати ним, роблячи його занадто великим, що призводить до перевищення витрат Gas у функції distribute_token.
Рекомендоване рішення полягає у використанні режиму "виведення".
для облікового запису в self.registered.iter)( {
let balance = self.accounts.get)&account(.unwrap)(;
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);
Проблема цього контракту полягає в тому, що якщо попередній учасник з найвищою ставкою скасує свій рахунок, нові учасники не зможуть успішно оновити стан.
Рішення полягає в реалізації адекватної обробки помилок, наприклад, тимчасове зберігання токенів, які не можна повернути, а пізніше їх можна буде забрати користувачем.
іржа
публічна структура Контракт {
// ...
pub lost_found: UnorderedMap<accountid, balance="">,
}
pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) {
match env::p romise_result(0) {
PromiseResult::Успішний(_) => {
// нормальна логіка повернення коштів
}
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((;
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(&аккаунт, &0);
ext_ft_token::ft_transfer(account, сума, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL);
}
}
3. Уникнення єдиної точки відмови
Втрата приватного ключа власника контракту призведе до неможливості використання ключових привілейованих функцій. Рішенням є використання механізму мультипідпису:
іржа
публічна структура Контракт {
власники пабів: Vec\u003caccountid\u003e,
required_confirmations пабу: U64,
операції з пабами: Vec,
}
let transaction = &mut self.transactions[transaction_id як usize];
transaction.confirmations += 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);
// Виконати логіку транзакції
}
Завдяки механізму мультипідпису можна уникнути паралічу контракту через втрату приватного ключа єдиного власника, підвищуючи рівень децентралізації та безпеки контракту.
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
7 лайків
Нагородити
7
6
Поділіться
Прокоментувати
0/400
TokenomicsTherapist
· 10год тому
Втратити закритий ключ занадто реально, ха-ха
Переглянути оригіналвідповісти на0
GasFeeNightmare
· 08-04 04:49
Не дарма це Великі інвестори Gas
Переглянути оригіналвідповісти на0
PretendingToReadDocs
· 08-04 04:49
DoS неможливо уникнути, про яку безпеку можна говорити?
Безпека смартконтрактів на Rust: 3 ключові стратегії захисту від атак DoS
Безпека смартконтрактів Rust: захист від атаки на відмову в обслуговуванні
атака на відмову в обслуговуванні(DoS) може призвести до того, що смартконтракти не зможуть нормально використовуватися протягом певного часу, а іноді навіть до їхнього постійного виходу з ладу. Основні причини включають:
Логіка контракту має дефект надмірної обчислювальної складності, що призводить до перевищення обмеження споживання Gas.
Залежність від стану виконання зовнішнього смартконтракту під час міжконтрактних викликів може бути заблокована зовнішнім контрактом.
Власник контракту втратив приватний ключ, не може виконати ключові функції привілеїв.
Нижче наведено аналіз з конкретними прикладами.
1. Уникайте обходу великих структур даних, які можуть бути керовані ззовні
Наступний є простим "бонусним" смартконтрактом, що містить ризик атаки на відмову в обслуговуванні:
ржавчина #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] публічна структура Контракт { pub зареєстровано: Vec, облікові записи pub: 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()); } else { 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");
}
Проблема цього смартконтракту полягає в тому, що розмір масиву registered не має обмеження, що дозволяє зловмисним користувачам маніпулювати ним, роблячи його занадто великим, що призводить до перевищення витрат Gas у функції distribute_token.
Рекомендоване рішення полягає у використанні режиму "виведення".
іржа pub fn distribute_token)&mut self, кількість: u128( { assert_eq!)env::p redecessor_account_id((, ДИСТРИБ'ЮТОР, "ERR_NOT_ALLOWED");
}
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);
}
!
2. Уникнення залежності стану при міжконтрактних викликах
Нижче наведено приклад "аукціонного" смартконтракту:
іржа #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub зареєстровано: Vec\u003caccountid\u003e, pub bid_price: UnorderedMap<accountid,balance>, pub current_leader: AccountId, highest_bid пабу: U128, }
pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue { стверджувати!(amount > self.highest_bid);
}
#( pub fn account_resolve)&mut self, sender_id: AccountId, amount: u128[private] { match env::p romise_result(0) { PromiseResult::Успішний(_) => { 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 = кількість; } PromiseResult::Failed => { ext_ft_token::ft_transfer) sender_id.clone)(, сума, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, (; log!)"Поверніть зараз"); } PromiseResult::NotReady => недосяжний!(), }; }
Проблема цього контракту полягає в тому, що якщо попередній учасник з найвищою ставкою скасує свій рахунок, нові учасники не зможуть успішно оновити стан.
Рішення полягає в реалізації адекватної обробки помилок, наприклад, тимчасове зберігання токенів, які не можна повернути, а пізніше їх можна буде забрати користувачем.
іржа публічна структура Контракт { // ... pub lost_found: UnorderedMap<accountid, balance="">, }
pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { match env::p romise_result(0) { PromiseResult::Успішний(_) => { // нормальна логіка повернення коштів } 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((;
}
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(&аккаунт, &0); ext_ft_token::ft_transfer(account, сума, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL); } }
3. Уникнення єдиної точки відмови
Втрата приватного ключа власника контракту призведе до неможливості використання ключових привілейованих функцій. Рішенням є використання механізму мультипідпису:
іржа публічна структура Контракт { власники пабів: Vec\u003caccountid\u003e, required_confirmations пабу: U64, операції з пабами: Vec, }
pub fn propose_transaction(&mut self, transaction: Transaction) { assert!019283746574839201self.owners.contains(&env::p redecessor_account_id)((, "Не власник"(; self.transactions.push)transaction); }
pub fn confirm_transaction)&mut self, transaction_id: u64( { assert!019283746574839201self.owners.contains)&env::p redecessor_account_id()(, "Не власник"(;
}
fn execute_transaction)&mut self, transaction_id: u64) { let transaction = self.transactions.remove(transaction_id як usize); // Виконати логіку транзакції }
Завдяки механізму мультипідпису можна уникнути паралічу контракту через втрату приватного ключа єдиного власника, підвищуючи рівень децентралізації та безпеки контракту.