Seguridad de contratos inteligentes en Rust: 3 estrategias clave para defenderse de ataques DoS

Seguridad de contratos inteligentes en Rust: defensa contra ataques de denegación de servicio

El ataque de denegación de servicio ( DoS ) puede causar que los contratos inteligentes no se puedan utilizar normalmente durante un período de tiempo, e incluso se pueden paralizar permanentemente. Las principales razones incluyen:

  1. La lógica del contrato tiene un defecto de complejidad computacional demasiado alta, lo que provoca que el consumo de Gas exceda el límite.

  2. La dependencia del estado de ejecución de contratos externos al realizar llamadas entre contratos puede ser bloqueada por el contrato externo.

  3. El propietario del contrato pierde la clave privada y no puede ejecutar funciones de privilegio clave.

A continuación, se realizará un análisis combinando ejemplos específicos.

1. Evitar recorrer grandes estructuras de datos que puedan ser manipuladas externamente

A continuación se muestra un contrato simple de "dividendo", 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(\u0026env::predecessor_account_id(), \u00260).is_some() { env::panic("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(), DISTRIBUTOR, "ERR_NOT_ALLOWED");

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!("Intente distribuir a la cuenta {}", &cur_account);
    
    ext_ft_token::ft_transfer(
        cur_account.clone(),
        cantidad,
        &FTTOKEN,
        0,
        GAS_FOR_SINGLE_CALL
    );
}

}

El problema del contrato es que no hay un límite en el tamaño del array registered, lo que puede ser manipulado por usuarios malintencionados para hacerlo demasiado grande, causando que la función distribute_token consuma Gas en exceso.

La solución recomendada es adoptar el modo de "retiro":

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

for account in 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(cuenta, cantidad, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL);

}

2. Evitar la dependencia de estado en las llamadas entre contratos

A continuación se muestra un ejemplo de contrato de "licitación":

ó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, }

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

if 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,
        cantidad,
        &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] { match env::promise_result(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)(, cantidad, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, (; log!)"Return Back Now"); } PromiseResult::NotReady => unreachable!(), }; }

El problema con este contrato es que, si el anterior postor más alto cancela su cuenta, los nuevos postores no podrán actualizar el estado con éxito.

La solución es implementar un manejo de errores razonable, por ejemplo, almacenar temporalmente los tokens que no se pueden devolver, y posteriormente permitir que el usuario los retire por sí mismo:

óxido pub struct Contract { // ... pub lost_found: UnorderedMap<accountid, balance="">, }

pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { coincidir env::promise_result(0) { PromiseResult::Successful(_) => { // Lógica de reembolso normal } PromiseResult::Failed => { // Almacenar temporalmente los tokens que no se pueden devolver let lost_amount = self.lost_found.get(&self.current_leader).unwrap_or(0); self.lost_found.insert(&self.current_leader, &)lost_amount + self.highest_bid((;

        // Actualizar 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(\u0026account).unwrap_or(0); si cantidad > 0 { self.lost_found.insert(&cuenta, &0); ext_ft_token::ft_transfer(cuenta, cantidad, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL); } }

3. Evitar puntos de falla únicos

La pérdida de la clave privada del propietario del contrato puede llevar a que funciones clave no estén disponibles. La solución es adoptar un mecanismo de firma múltiple:

óxido pub struct Contract { propietarios de pub: Vec\u003caccountid\u003e, pub required_confirmations: u64, pub transactions: Vec, }

pub fn propose_transaction(&mut self, transaction: Transaction) { assert!(self.owners.contains)&env::predecessor_account_id(((, "No es un propietario"); self.transactions.push)transaction); }

pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains)&env::predecessor_account_id(((, "No es un propietario");

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

if 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); // Ejecutar la lógica de la transacción }

A través del mecanismo de firma múltiple, se puede evitar que la pérdida de la clave privada de un solo propietario cause la paralización del contrato, mejorando el grado de descentralización y la seguridad del contrato.

</accountid,></accountid,balance></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.
  • Recompensa
  • 6
  • Compartir
Comentar
0/400
TokenomicsTherapistvip
· hace12h
Perder la llave privada es demasiado real, jaja
Ver originalesResponder0
GasFeeNightmarevip
· 08-04 04:49
No es de extrañar que sea un Grande inversor de Gas
Ver originalesResponder0
PretendingToReadDocsvip
· 08-04 04:49
No se puede evitar el DoS, ¿qué más seguridad se puede discutir?
Ver originalesResponder0
HashRatePhilosophervip
· 08-04 04:32
¡La gran armada de rust ha llegado, que tiemble el oponente!
Ver originalesResponder0
TestnetFreeloadervip
· 08-04 04:28
防dos你搁这 trampa 理论呢
Ver originalesResponder0
MevShadowrangervip
· 08-04 04:26
La tarifa de Gas está por las nubes, me tiene desesperado.
Ver originalesResponder0
  • Anclado
Opere con criptomonedas en cualquier momento y lugar
qrCode
Escanee para descargar la aplicación Gate
Comunidad
Español
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)