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:
A lógica do contrato tem um defeito de complexidade de cálculo excessiva, levando o consumo de Gas a ultrapassar o limite.
A dependência do estado de execução de contratos externos durante chamadas entre contratos pode ser bloqueada por contratos externos.
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:
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":
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.
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:
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.
7 Curtidas
Recompensa
7
6
Compartilhar
Comentário
0/400
TokenomicsTherapist
· 5h atrás
Perder a chave privada é muito real, haha
Ver originalResponder0
GasFeeNightmare
· 08-04 04:49
Não é à toa que é um Grande investidor de Gas
Ver originalResponder0
PretendingToReadDocs
· 08-04 04:49
DoS não pode ser evitado, o que mais há para falar sobre segurança?
Ver originalResponder0
HashRatePhilosopher
· 08-04 04:32
A grande armada rust está chegando, que os adversários tremam.
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:
A lógica do contrato tem um defeito de complexidade de cálculo excessiva, levando o consumo de Gas a ultrapassar o limite.
A dependência do estado de execução de contratos externos durante chamadas entre contratos pode ser bloqueada por contratos externos.
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");
}
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");
}
pub fn withdraw(&mut self) { let account = env::predecessor_account_id(); let amount = self.accounts.get(&account).unwrap(); self.accounts.insert(&account, &0);
}
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);
}
#( 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((;
}
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");
}
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.