Seguridad de aplicaciones Rust: top 10 vulnerabilidades para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Seguridad de aplicaciones Rust: top 10 vulnerabilidades para principiantes



Rust se posiciona como un lenguaje de sistema seguro que previene clases enteras de vulnerabilidades en la etapa de compilación. Sin embargo, esto no significa que las aplicaciones Rust sean absolutamente invulnerables. Los desarrolladores principiantes a menudo cometen errores que pueden llevar a serios problemas de seguridad. En este artículo analizaremos las top 10 vulnerabilidades en aplicaciones Rust que todo novato debe conocer.



1. Uso incorrecto de código unsafe



Los bloques unsafe son la causa más frecuente de vulnerabilidades en Rust. Desactivan las comprobaciones de seguridad, lo que puede provocar desreferencias de punteros nulos, condiciones de carrera y daños en la memoria.



// Ejemplo peligrosounsafe {    let ptr = std::ptr::null::<i32>();    println!("{}", *ptr); // UB! Segmentation fault}

// Versión segurafn safe_access(ptr: *const i32) -> Option<i32> { if ptr.is_null() { return None; } unsafe { Some(*ptr) }}


Recomendación: Minimice el uso de unsafe. Si es necesario, aíslelo en funciones pequeñas y documente cuidadosamente los invariantes.



2. Condiciones de carrera a través de Send y Sync



Rust protege contra condiciones de carrera mediante el sistema de propiedad, pero una implementación incorrecta de los traits Send y Sync puede romper esta protección.



use std::cell::UnsafeCell;use std::sync::Arc;

struct UnsafeCounter(UnsafeCell<u32>);

// Implementación peligrosaunsafe impl Send for UnsafeCounter {}unsafe impl Sync for UnsafeCounter {}

fn main() { let counter = Arc::new(UnsafeCounter(UnsafeCell::new(0))); let mut handles = vec![]; for _ in 0..10 { let c = counter.clone(); handles.push(std::thread::spawn(move || { // ¡Condición de carrera! unsafe { *c.0.get() += 1; } })); }}


Recomendación: Use Arc<Mutex<T>> o Arc<RwLock<T>> en lugar de implementar la sincronización manualmente.



3. Deserialización insegura de datos



Al deserializar datos de fuentes externas (JSON, YAML, bincode), un atacante puede inyectar un payload especialmente diseñado.



use serde::Deserialize;

#[derive(Deserialize)]struct Config { path: String, buffer_size: usize,}

fn load_config(data: &str) -> Result<Config, serde_json::Error> { // Vulnerabilidad potencial: números demasiado grandes let config: Config = serde_json::from_str(data)?; if config.buffer_size > 1_000_000 { return Err(serde_json::Error::custom("Buffer too large")); } Ok(config)}


Recomendación: Siempre verifique los límites de los valores después de la deserialización. Use #[serde(deserialize_with = "...")] para validadores personalizados.



4. Fugas de memoria a través de Box<dyn Error>



Devolver frecuentemente Box<dyn Error> puede provocar fugas de memoria, especialmente al trabajar con grandes volúmenes de datos.



// Enfoque problemáticofn process_data(data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {    // ... procesamiento    Ok(())}

// Mejor enfoque#[derive(Debug)]enum AppError { InvalidData(String), IoError(std::io::Error),}

fn process_data_safe(data: &[u8]) -> Result<(), AppError> { // ... procesamiento Ok(())}


Recomendación: Cree sus propios tipos de error usando thiserror o anyhow para un mejor rendimiento y seguridad.



5. Desbordamiento de búfer en FFI



Al interactuar con código C a través de FFI (Foreign Function Interface), es fácil cometer

Blogs

Book Recommendations