Segurança de aplicações Rust: top-10 vulnerabilidades para iniciantes
\n\nRust é considerada uma das linguagens de programação mais seguras graças ao seu sistema de ownership (propriedade) e tipagem estrita. No entanto, mesmo em Rust é possível cometer erros, especialmente ao usar blocos unsafe ou ao trabalhar incorretamente com bibliotecas externas. Neste artigo, analisaremos as top-10 vulnerabilidades que desenvolvedores iniciantes em Rust enfrentam e mostraremos como evitá-las.
1. Uso inseguro de código unsafe
\n\nA principal característica do Rust é a segurança de memória sem coletor de lixo. No entanto, a palavra-chave unsafe desativa algumas verificações do compilador. Iniciantes frequentemente abusam dela sem entender as consequências.
// Perigoso: desreferenciar ponteiro bruto sem verificação\nfn dangerous() {\n let ptr = 0x12345 as *const i32;\n unsafe {\n println!("Valor: {}", *ptr); // Pode causar segfault\n }\n}\n\n// Seguro: usar métodos verificados\nfn safe() {\n let x = 42;\n let ptr = &x as *const i32;\n unsafe {\n if !ptr.is_null() {\n println!("Valor: {}", *ptr);\n }\n }\n}\n\nRecomendação: Use unsafe apenas quando realmente necessário e sempre verifique se os ponteiros são nulos.
2. Vazamentos de memória via Box::leak e ciclos Rc
\n\nEmbora Rust previna a maioria dos vazamentos de memória, referências cíclicas com Rc ou RefCell podem causar vazamentos. Box::leak também é perigoso, pois "esquece" intencionalmente a memória.
use std::rc::Rc;\nuse std::cell::RefCell;\n\n#[derive(Debug)]\nstruct Node {\n value: i32,\n next: Option<Rc<RefCell<Node>>>,\n}\n\nfn create_cycle() {\n let a = Rc::new(RefCell::new(Node { value: 1, next: None }));\n let b = Rc::new(RefCell::new(Node { value: 2, next: None }));\n \n // Criando ciclo: a -> b -> a\n a.borrow_mut().next = Some(b.clone());\n b.borrow_mut().next = Some(a.clone());\n // A memória nunca será liberada!\n}\n\nSolução: Use Weak<T> para quebrar referências cíclicas ou repense a arquitetura.
3. Condições de corrida (Data Races) em código multithread
\n\nRust garante a ausência de data races em código seguro, mas o uso incorreto de unsafe ou das traits Send/Sync pode causar problemas.
use std::thread;\nuse std::sync::Arc;\nuse std::sync::Mutex;\n\n// Perigoso: compartilhar estado mutável sem sincronização\nfn data_race_example() {\n let mut counter = 0;\n let mut handles = vec![];\n \n for _ in 0..10 {\n handles.push(thread::spawn(|| {\n counter += 1; // Erro de compilação: cannot borrow\n }));\n }\n}\n\n// Seguro: usar Mutex\nfn safe_mutex() {\n let counter = Arc::new(Mutex::new(0));\n let mut handles = vec![];\n \n for _ in 0..10 {\n let counter = Arc::clone(&counter);\n handles.push(thread::spawn(move || {\n let mut num = counter.lock().unwrap();\n *num += 1;\n }));\n }\n}\n\n4. Estouro de buffer (Buffer Overflow)
\n\nEm Rust, o estouro de buffer é improvável em código seguro, mas possível ao usar unsafe ou ao trabalhar com FFI (Foreign Function Interface).
// Perigoso: cópia insegura para buffer\nfn unsafe_copy(src: &[u8], dst: &mut [u8]) {\n unsafe {\n std::ptr::copy_nonoverlapping(\n src.as_ptr(),\n dst.as_mut_ptr(),\n src.len() // Se src.len() > dst.len() - estouro!\n );\n }\n}\n\n// Seguro: usar slice::copy_from_slice\nfn safe_copy(src: &[u8], dst: &mut [u8]) {\n if src.len() <= dst.len