Ataques de denegación de servicio en contratos inteligentes de Rust
Un ataque de denegación de (DoS) de servicio puede hacer que un contrato inteligente quede inutilizable durante un período de tiempo. Hay varias razones para esto:
En la lógica del contrato existe un defecto de complejidad de cálculo excesiva, lo que provoca que el consumo de Gas supere el límite.
Al llamar a contratos cruzados, la ejecución del contrato depende del estado de contratos externos no confiables, lo que provoca un bloqueo.
El propietario del contrato pierde la llave privada, lo que impide la ejecución de funciones de privilegio clave.
A continuación, analizaremos estas vulnerabilidades de DoS a través de ejemplos concretos.
1. Atraviese grandes estructuras de datos que se pueden cambiar externamente
A continuación se muestra un "contrato inteligente" de "dividendos" que presenta un riesgo de ataque de denegación de servicio:
for cur_account in 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!("Intenta distribuir a la cuenta {}", &cur_account);
ext_ft_token::ft_transfer(
cur_account.clone(),
cantidad,
&FTTOKEN,
0,
GAS_FOR_SINGLE_CALL
);
}
}
En este caso, el autoregistro puede expandirse indefinidamente, lo que resulta en un gas insuficiente durante el recorrido.
Debería cambiarse al modo "retiro" para que los usuarios puedan extraer activamente las recompensas:
óxido
pub fn withdraw(&mut self) {
let account_id = env::predecessor_account_id();
let amount = self.accounts.get(&account_id).expect("No reward");
Si la cuenta del anterior postor ha sido destruida, la nueva oferta será bloqueada.
Se deben considerar los casos de fallo en las llamadas externas; se pueden almacenar temporalmente los tokens no reembolsables para que los usuarios los retiren posteriormente:
óxido
pub fn withdraw_lost_funds(&mut self) {
let account_id = env::predecessor_account_id();
let amount = self.lost_funds.get(\u0026account_id).expect("No se han perdido fondos");
Ciertas funciones clave solo pueden ser llamadas por el propietario del contrato. Si el propietario pierde la clave privada, estas funciones no podrán ejecutarse.
Se debe adoptar un esquema de múltiples firmas para gestionar los contratos, evitando fallos de punto único:
pub fn submit_transaction(&mut self, transacción: Transaction) {
assert!(self.owners.contains(&env::p redecessor_account_ id()));
// Agregar transacciones a la lista de espera de confirmación
}
pub fn confirm_transaction(&mut self, transaction_id: u64) {
assert!(self.owners.contains(&env::p redecessor_account_ id()));
Aumentar el número de confirmaciones
// Si el número de confirmaciones alcanza el requisito, ejecutar la transacción
}
A través de los métodos anteriores se pueden prevenir eficazmente los riesgos de ataque de denegación de servicio en los contratos inteligentes.
<accountid,<><accountid,<>
Ver originales
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
Guía práctica para defender contratos inteligentes Rust de ataques DoS
Ataques de denegación de servicio en contratos inteligentes de Rust
Un ataque de denegación de (DoS) de servicio puede hacer que un contrato inteligente quede inutilizable durante un período de tiempo. Hay varias razones para esto:
En la lógica del contrato existe un defecto de complejidad de cálculo excesiva, lo que provoca que el consumo de Gas supere el límite.
Al llamar a contratos cruzados, la ejecución del contrato depende del estado de contratos externos no confiables, lo que provoca un bloqueo.
El propietario del contrato pierde la llave privada, lo que impide la ejecución de funciones de privilegio clave.
A continuación, analizaremos estas vulnerabilidades de DoS a través de ejemplos concretos.
1. Atraviese grandes estructuras de datos que se pueden cambiar externamente
A continuación se muestra un "contrato inteligente" de "dividendos" que presenta un riesgo de ataque de denegación de servicio:
óxido #[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::p redecessor_account_id(), &0).is_ some() { env::p anic("La cuenta ya está registrada".to_string().as_ bytes()); } else { self.registered.push(env::predecessor_account_id()); } log!("Cuenta registrada {}", env::predecessor_account_id()); }
pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::predecessor_account_id(), DISTRIBUIDOR, "ERR_NOT_ALLOWED");
}
En este caso, el autoregistro puede expandirse indefinidamente, lo que resulta en un gas insuficiente durante el recorrido.
Debería cambiarse al modo "retiro" para que los usuarios puedan extraer activamente las recompensas:
óxido pub fn withdraw(&mut self) { let account_id = env::predecessor_account_id(); let amount = self.accounts.get(&account_id).expect("No reward");
}
2. La dependencia de estado entre contratos cruzados causa bloqueo
Aquí hay un contrato de "Subasta":
óxido #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { pub registrado: Vec, pub bid_price: UnorderedMap\u003caccountid, balance=""\u003e, pub current_leader: AccountId, pub highest_bid: u128, Reembolso del pub: Bool }
pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue { ¡Afirmar!(amount > self.highest_bid);
}
#[private] pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { match env::promise_result(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 = cantidad; } PromiseResult::Failed => { ext_ft_token::ft_transfer( sender_id.clone(), cantidad, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, ); log!("Regresa Ahora"); } }; }
Si la cuenta del anterior postor ha sido destruida, la nueva oferta será bloqueada.
Se deben considerar los casos de fallo en las llamadas externas; se pueden almacenar temporalmente los tokens no reembolsables para que los usuarios los retiren posteriormente:
óxido pub fn withdraw_lost_funds(&mut self) { let account_id = env::predecessor_account_id(); let amount = self.lost_funds.get(\u0026account_id).expect("No se han perdido fondos");
}
3. Pérdida de la clave privada del propietario
Ciertas funciones clave solo pueden ser llamadas por el propietario del contrato. Si el propietario pierde la clave privada, estas funciones no podrán ejecutarse.
Se debe adoptar un esquema de múltiples firmas para gestionar los contratos, evitando fallos de punto único:
óxido pub struct MultiSigContract { propietarios: Vec\u003caccountid\u003e, required_confirmations: u32, }
pub fn submit_transaction(&mut self, transacción: Transaction) { assert!(self.owners.contains(&env::p redecessor_account_ id())); // Agregar transacciones a la lista de espera de confirmación }
pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains(&env::p redecessor_account_ id())); Aumentar el número de confirmaciones // Si el número de confirmaciones alcanza el requisito, ejecutar la transacción }
A través de los métodos anteriores se pueden prevenir eficazmente los riesgos de ataque de denegación de servicio en los contratos inteligentes.