Segurança de aplicações C++: top 10 vulnerabilidades e formas de proteção
C++ é uma linguagem de programação de sistemas poderosa, mas sua flexibilidade e acesso direto à memória tornam as aplicações vulneráveis a ataques graves. De acordo com OWASP e CWE, muitas vulnerabilidades críticas em software industrial (de jogos a aviônica) surgem precisamente devido a erros no código C++. Neste artigo, analisaremos as 10 vulnerabilidades mais perigosas, forneceremos exemplos de código e mostraremos como evitá-las.
1. Estouro de buffer (Buffer Overflow)
Vulnerabilidade clássica que permite ao invasor sobrescrever áreas adjacentes da memória. Ocorre ao copiar dados sem verificar o tamanho.
// Código vulnerável#include <cstring>void vulnerable(char* input) { char buffer[10]; strcpy(buffer, input); // Perigo: se input for maior que 9 caracteres}// Versão seguravoid secure(char* input, size_t len) { char buffer[10]; if (len < sizeof(buffer)) { strncpy(buffer, input, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\\0'; }}Proteção: use std::string, std::vector ou funções com limite de comprimento (strncpy, snprintf). Ative as flags do compilador -fstack-protector-strong e -D_FORTIFY_SOURCE=2.
2. Uso após liberação (Use-After-Free)
Acesso à memória que já foi liberada. Leva à leitura/escrita em área alheia ou à execução de código arbitrário.
// Código vulnerávelint* ptr = new int(42);delete ptr;*ptr = 100; // Erro! ptr não é mais válido// Versão seguraint* ptr = new int(42);delete ptr;ptr = nullptr; // Zeramos o ponteiroif (ptr) *ptr = 100; // Verificação antes do usoProteção: use smart pointers (std::unique_ptr, std::shared_ptr), zere raw pointers após delete, aplique sanitizers (-fsanitize=address).
3. Estouro de inteiro (Integer Overflow)
Ultrapassar os limites da faixa permitida em operações aritméticas. Frequentemente usado para contornar verificações de tamanho de buffer.
// Código vulnerávelsize_t size = 0;size_t user_input = 4294967295; // 2^32 - 1size_t total = size + user_input + 1; // Estouro!// Versão segura com verificaçãoif (user_input > SIZE_MAX - size) { throw std::overflow_error("Tamanho muito grande");}size_t total = size + user_input + 1;Proteção: use tipos de largura fixa (uint32_t), verifique o estouro antes da operação, aplique __builtin_add_overflow (GCC/Clang).
4. Desreferência de ponteiro nulo (Null Pointer Dereference)
Tentativa de acessar a memória no endereço NULL. Leva ao encerramento abrupto do programa (segfault) ou a uma vulnerabilidade DoS.
// Código vulnerávelvoid process(int* ptr) { *ptr = 10; // Se ptr == nullptr — crash}// Versão seguravoid process(int* ptr) { if (!ptr) { throw std::invalid_argument("Ponteiro nulo recebido"); } *ptr = 10;}Proteção: sempre verifique ponteiros para nullptr antes do uso, retorne std::optional em vez de raw pointers.
5. Vazamentos de memória (Memory Leaks)
Perda de acesso à memória alocada sem sua liberação. Com o tempo, esgota os recursos do sistema.
// Código vulnerávelvoid leak() { int* data = new int[100]; // Esquecemos delete[] data — vazamento}// Versão seguravoid no_leak() { std::unique_ptr<int[]> data = std::make_unique<int[]>(100); // A memória será liberada automaticamente}Proteção: use RAII (smart pointers, containers