Segurança de contratos inteligentes Rust: 3 principais estratégias para defender ataques DoS

Segurança de contratos inteligentes Rust: defesa contra ataque de negação de serviço

ataque de negação de serviço(DoS) pode levar a contratos inteligentes a não serem usados normalmente por um período de tempo, ou até mesmo a uma paralisia permanente. As principais causas incluem:

  1. A lógica do contrato tem um defeito de complexidade de cálculo excessiva, levando o consumo de Gas a ultrapassar o limite.

  2. A dependência do estado de execução de contratos externos durante chamadas entre contratos pode ser bloqueada por contratos externos.

  3. O proprietário do contrato perdeu a chave privada, não conseguindo executar funções privilegiadas essenciais.

Abaixo, faremos uma análise com exemplos concretos.

1. Evitar percorrer grandes estruturas de dados que podem ser controladas externamente

Segue um contrato simples de "dividendo", que apresenta risco de ataque de negação de serviço:

ferrugem #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registered: Vec\u003caccountid\u003e, pub accounts: UnorderedMap\u003caccountid, balance=""\u003e, }

pub fn register_account(&mut self) { se self.accounts.insert(\u0026env::predecessor_account_id(), \u00260).is_some() { env::panic("A conta já está registrada".to_string().as_bytes()); } else { self.registered.push(env::predecessor_account_id()); } log!("Conta registrada {}", env::predecessor_account_id()); }

pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::predecessor_account_id(), DISTRIBUTOR, "ERR_NOT_ALLOWED");

for cur_account in self.registered.iter() {
    let balance = self.accounts.get(\u0026cur_account).expect("ERR_GET");
    self.accounts.insert(\u0026cur_account, \u0026balance.checked_add(amount).expect("ERR_ADD"));
    log!("Tente distribuir para a conta {}", &cur_account);
    
    ext_ft_token::ft_transfer(
        cur_account.clone(),
        montante,
        &FTTOKEN,
        0,
        GAS_FOR_SINGLE_CALL
    );
}

}

O problema do contrato é que o tamanho do array registered não tem limite, podendo ser manipulado por usuários mal-intencionados para se tornar muito grande, levando ao consumo excessivo de Gas na função distribute_token.

A solução recomendada é adotar o modo "levantamento":

ferrugem pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::predecessor_account_id(), DISTRIBUTOR, "ERR_NOT_ALLOWED");

para conta em self.registered.iter() {
    let balance = self.accounts.get(&account).unwrap();
    self.accounts.insert(&account, &(balance + amount));
}

}

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

ext_ft_token::ft_transfer(conta, montante, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL);

}

2. Evitar a dependência de estado em chamadas entre contratos

Abaixo está um exemplo de contrato "de leilão":

ferrugem #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registrado: Vec\u003caccountid\u003e, pub bid_price: UnorderedMap\u003caccountid,balance\u003e, pub current_leader: AccountId, pub highest_bid: u128, }

PromiseOrValue { assert!(amount > self.highest_bid);

se self.current_leader == DEFAULT_ACCOUNT {
    self.current_leader = sender_id;
    self.highest_bid = amount;
} else {
    ext_ft_token::account_exist(
        self.current_leader.clone)(,
        &FTTOKEN,
        0,
        env::prepaid_gas() - GAS_FOR_SINGLE_CALL * 4,
    (.then)ext_self::account_resolve)
        sender_id,
        montante,
        &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] { correspondência env::resultado_promessa(0) { PromiseResult::Successful(_) => { 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 = amount; } PromiseResult::Failed => { ext_ft_token::ft_transfer) sender_id.clone)(, quantia, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, (; log!)"Return Back Now"); } PromiseResult::NotReady => unreachable!(), }; }

O problema deste contrato é que, se o anterior licitante mais alto cancelar a conta, os novos licitantes não conseguirão atualizar o estado com sucesso.

A solução é implementar um tratamento de erros razoável, como armazenar temporariamente os tokens que não podem ser devolvidos, para que o usuário possa retirá-los posteriormente.

ferrugem pub struct Contract { // ... pub lost_found: UnorderedMap\u003caccountid, balance=""\u003e, }

pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { corresponder env::promise_result(0) { PromiseResult::Successful(_) => { // lógica de reembolso normal } PromiseResult::Failed => { // Armazenar tokens não recuperáveis let lost_amount = self.lost_found.get(&self.current_leader).unwrap_or(0); self.lost_found.insert(\u0026self.current_leader, \u0026)lost_amount + self.highest_bid((;

        // Atualizar estado
        self.current_leader = sender_id;
        self.highest_bid = amount;
    }
    PromiseResult::NotReady => unreachable!)),
}

}

pub fn withdraw_lost_found(&mut self) { let account = env::predecessor_account_id(); let amount = self.lost_found.get(&account).unwrap_or(0); se a quantidade > 0 { self.lost_found.insert(&account, &0); ext_ft_token::ft_transfer(account, amount, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL); } }

3. Evitar falhas de ponto único

A perda da chave privada do proprietário do contrato pode resultar na impossibilidade de utilizar funções de privilégios críticos. A solução é adotar um mecanismo de múltiplas assinaturas:

ferrugem pub struct Contract { proprietários de pub: Vec\u003caccountid\u003e, pub required_confirmations: u64, pub transactions: Vec, }

pub fn propose_transaction(&mut self, transaction: Transaction) { assert!(self.owners.contains)\u0026env::predecessor_account_id(((, "Not an owner"); self.transactions.push)transaction); }

pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains)&env::predecessor_account_id(((, "Not an owner");

let transaction = &mut self.transactions[transaction_id as usize];
transaction.confirmations += 1;

se 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 as usize); // Executar lógica de transação }

Através do mecanismo de múltiplas assinaturas, é possível evitar a paralisação do contrato devido à perda da chave privada de um único proprietário, aumentando o grau de descentralização e segurança do contrato.

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

Ver original
Esta página pode conter conteúdo de terceiros, que é fornecido apenas para fins informativos (não para representações/garantias) e não deve ser considerada como um endosso de suas opiniões pela Gate nem como aconselhamento financeiro ou profissional. Consulte a Isenção de responsabilidade para obter detalhes.
  • Recompensa
  • 6
  • Compartilhar
Comentário
0/400
TokenomicsTherapistvip
· 5h atrás
Perder a chave privada é muito real, haha
Ver originalResponder0
GasFeeNightmarevip
· 08-04 04:49
Não é à toa que é um Grande investidor de Gas
Ver originalResponder0
PretendingToReadDocsvip
· 08-04 04:49
DoS não pode ser evitado, o que mais há para falar sobre segurança?
Ver originalResponder0
HashRatePhilosophervip
· 08-04 04:32
A grande armada rust está chegando, que os adversários tremam.
Ver originalResponder0
TestnetFreeloadervip
· 08-04 04:28
防dos你搁这armadilha理论呢
Ver originalResponder0
MevShadowrangervip
· 08-04 04:26
O custo do gás está absurdo, fiquei maluco.
Ver originalResponder0
  • Marcar
Faça trade de criptomoedas em qualquer lugar e a qualquer hora
qrCode
Escaneie o código para baixar o app da Gate
Comunidade
Português (Brasil)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)