Ataques de negação de serviço em contratos inteligentes Rust
O ataque de negação de serviço ( DoS ) pode fazer com que os contratos inteligentes não possam ser utilizados normalmente durante um período de tempo. As principais razões são as seguintes:
Existe um defeito na lógica do contrato que resulta em complexidade de cálculo excessiva, levando ao consumo de Gas a ultrapassar o limite.
Durante a chamada entre contratos, a execução do contrato depende do estado de contratos externos não fiáveis, causando bloqueio.
O proprietário do contrato perdeu a chave privada, resultando na impossibilidade de executar funções privilegiadas críticas.
A seguir, analisaremos essas vulnerabilidades DoS através de exemplos concretos.
1. Percorra grandes estruturas de dados que podem ser alteradas externamente
Aqui está um contrato simples de "dividendo", que apresenta risco de ataque de negação de serviço:
for cur_account in self.registered.iter() {
let balance = self.accounts.get(&cur_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
);
}
}
Aqui self.registered pode ser expandido indefinidamente, levando a falta de Gas durante a iteração.
Deve-se usar o modo "saque" para que os usuários retirem ativamente as recompensas:
ferrugem
pub fn withdraw(&mut self) {
let account_id = env::predecessor_account_id();
let amount = self.accounts.get(&account_id).expect("Sem recompensa");
Algumas funções essenciais só podem ser chamadas pelo proprietário do contrato. Se o proprietário perder a chave privada, essas funções não serão executadas.
Deve ser utilizado um sistema de assinaturas múltiplas para gerir contratos, a fim de evitar um único ponto de falha:
pub fn submit_transaction(&mut self, transação: Transaction) {
assert!(self.owners.contains)&env::p redecessor_account_ id((();
Adicionar a transação à lista pendente
}
pub fn confirm_transaction)&mut self, transaction_id: u64) {
assert!(self.owners.contains)&env::p redecessor_account_ id((();
// Aumentar o número de confirmações
// Se o número de confirmações atingir o requisito, executar a transação
}
Através dos métodos acima, é possível prevenir eficazmente o risco de ataque de negação de serviço em contratos inteligentes.
Esta página pode conter conteúdos de terceiros, que são fornecidos apenas para fins informativos (sem representações/garantias) e não devem ser considerados como uma aprovação dos seus pontos de vista pela Gate, nem como aconselhamento financeiro ou profissional. Consulte a Declaração de exoneração de responsabilidade para obter mais informações.
23 gostos
Recompensa
23
7
Partilhar
Comentar
0/400
ConsensusDissenter
· 07-19 04:59
insights valiosos值得收藏学习
Ver originalResponder0
MEVHunterZhang
· 07-18 23:52
Não é à toa que é um conselho baseado na experiência.
Guia prático para defesa de ataques DoS em contratos inteligentes Rust
Ataques de negação de serviço em contratos inteligentes Rust
O ataque de negação de serviço ( DoS ) pode fazer com que os contratos inteligentes não possam ser utilizados normalmente durante um período de tempo. As principais razões são as seguintes:
Existe um defeito na lógica do contrato que resulta em complexidade de cálculo excessiva, levando ao consumo de Gas a ultrapassar o limite.
Durante a chamada entre contratos, a execução do contrato depende do estado de contratos externos não fiáveis, causando bloqueio.
O proprietário do contrato perdeu a chave privada, resultando na impossibilidade de executar funções privilegiadas críticas.
A seguir, analisaremos essas vulnerabilidades DoS através de exemplos concretos.
1. Percorra grandes estruturas de dados que podem ser alteradas externamente
Aqui está 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) { if self.accounts.insert(&env::predecessor_account_id(), &0).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::p redecessor_account_id(), DISTRIBUIDOR, "ERR_NOT_ALLOWED");
}
Aqui self.registered pode ser expandido indefinidamente, levando a falta de Gas durante a iteração.
Deve-se usar o modo "saque" para que os usuários retirem ativamente as recompensas:
ferrugem pub fn withdraw(&mut self) { let account_id = env::predecessor_account_id(); let amount = self.accounts.get(&account_id).expect("Sem recompensa");
}
2. Dependências de estado entre contratos levam ao bloqueio
Segue um contrato de "licitação":
ferrugem #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registado: Vec, pub bid_price: UnorderedMap\u003caccountid, balance=""\u003e, pub current_leader: AccountId, Pub highest_bid: U128, Reembolso Pub: Bool }
PromiseOrValue { afirmar!(amount > self.highest_bid);
}
#( pub fn account_resolve)&mut self, sender_id: AccountId, amount: u128[private] { correspondência env::resultado_promessa(0) { PromiseResult::NotReady => unreachable!(), 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 = montante; } PromiseResult::Failed => { ext_ft_token::ft_transfer) sender_id.clone)(, quantia, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, (; log!)"Return Back Now"); } }; }
Se a conta do licitante anterior for destruída, a nova oferta será bloqueada.
Considerando a falha de chamadas externas, tokens não retornáveis podem ser preparados para retirada subsequente:
ferrugem pub fn withdraw_lost_funds(&mut self) { vamos account_id = env::p redecessor_account_id(); let amount = self.lost_funds.get(&account_id).expect("No lost funds");
}
3. Chave privada do proprietário perdida
Algumas funções essenciais só podem ser chamadas pelo proprietário do contrato. Se o proprietário perder a chave privada, essas funções não serão executadas.
Deve ser utilizado um sistema de assinaturas múltiplas para gerir contratos, a fim de evitar um único ponto de falha:
ferrugem pub struct MultiSigContract { proprietários: Vec\u003caccountid\u003e, required_confirmations: u32, }
pub fn submit_transaction(&mut self, transação: Transaction) { assert!(self.owners.contains)&env::p redecessor_account_ id(((); Adicionar a transação à lista pendente }
pub fn confirm_transaction)&mut self, transaction_id: u64) { assert!(self.owners.contains)&env::p redecessor_account_ id(((); // Aumentar o número de confirmações // Se o número de confirmações atingir o requisito, executar a transação }
Através dos métodos acima, é possível prevenir eficazmente o risco de ataque de negação de serviço em contratos inteligentes.
![])https://img-cdn.gateio.im/webp-social/moments-7076cf1226a2276d1e4cd994d259841f.webp)</accountid,></accountid,>