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

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

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

  1. Проблема сложности вычислений в логике смарт-контрактов приводит к превышению лимита потребления Gas.

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

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

Ниже мы проанализируем уязвимости атак типа «отказ в обслуживании» и их решения через несколько конкретных примеров.

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

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

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

impl Контракт { 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 расход газа превышает лимит.

Рекомендуемое решение:

  1. Ограничить размер массива registered.

  2. Использовать режим "вывода средств", позволяя пользователям самостоятельно забирать вознаграждения, а не активному распределению со стороны смарт-контрактов.

!

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

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

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

impl Контракт { 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,
        ));
    }

    бревно!(
        "текущий_лидер: {} высшая_ставка: {}", 
        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!("Вернуться сейчас");
        }
    };
}

}

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

Рекомендуемое решение:

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

3. Утрата приватного ключа владельца

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

Рекомендуемое решение:

  1. Установите несколько владельцев контракта для совместного управления.

  2. Использование механизма многоподписей вместо контроля единственного владельца.

  3. Реализация децентрализованного механизма управления контрактами.

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

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

Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • 8
  • Поделиться
комментарий
0/400
MEVHunterWangvip
· 12ч назад
Портить контракт — это не проблема, когда всё под контролем.
Посмотреть ОригиналОтветить0
ProofOfNothingvip
· 16ч назад
Опять формализм и пустая болтовня.
Посмотреть ОригиналОтветить0
LuckyHashValuevip
· 20ч назад
Атака не была отбита, снова будет падение до нуля.
Посмотреть ОригиналОтветить0
ColdWalletGuardianvip
· 20ч назад
Блокчейн入门标配知识点
Посмотреть ОригиналОтветить0
SelfSovereignStevevip
· 20ч назад
Закрытый ключ потерян, будет ужасно
Посмотреть ОригиналОтветить0
StableBoivip
· 20ч назад
Этот контракт действительно довольно рискованный, стабильность не на высоте.
Посмотреть ОригиналОтветить0
Ser_This_Is_A_Casinovip
· 20ч назад
Ах, Rust действительно трудно, лучше рассмотреть Solidity.
Посмотреть ОригиналОтветить0
GasFeeThundervip
· 20ч назад
Вы едите слишком много газа и становитесь волосатым? Рано или поздно контракт будет разорван
Посмотреть ОригиналОтветить0
  • Закрепить