Seguridad de aplicaciones C++: top 10 vulnerabilidades para principiantes
\n\nEl lenguaje C++ le da al desarrollador un enorme control sobre la memoria y el rendimiento, pero esta libertad conlleva una gran responsabilidad. Según OWASP y CWE, las vulnerabilidades en las aplicaciones C++ a menudo surgen debido a una gestión incorrecta de la memoria, la falta de verificación de límites y la falta de atención al trabajar con punteros. En este artículo, analizaremos las 10 vulnerabilidades más comunes que enfrentan los principiantes. Aprenderá cómo detectarlas y, lo más importante, cómo evitarlas.
\n\n1. Desbordamiento de búfer (Buffer Overflow)
\nUn clásico del género. Esta vulnerabilidad ocurre cuando el programa escribe datos fuera de los límites del búfer asignado. Un atacante puede sobrescribir áreas de memoria adyacentes, incluida la dirección de retorno de la función, y obtener control sobre la ejecución del código.
\n\n// Código vulnerable\nvoid vulnerable() {\n char buffer[10];\n std::cout << "Ingrese una cadena: ";\n std::cin >> buffer; // ¡Sin verificación de longitud!\n std::cout << buffer << std::endl;\n}\n\n// Versión segura\nvoid safe() {\n char buffer[10];\n std::cout << "Ingrese una cadena: ";\n std::cin.getline(buffer, sizeof(buffer));\n std::cout << buffer << std::endl;\n}\n\nCómo protegerse: Utilice siempre funciones con limitación de longitud (strncpy, snprintf, std::string). Evite gets() y strcpy().
2. Fugas de memoria (Memory Leaks)
\nSi asignó memoria con new pero olvidó liberarla con delete, el programa irá "consumiendo" memoria RAM gradualmente. Para aplicaciones de servidor, esto es un desastre: después de unas horas de funcionamiento, el servicio fallará con el error std::bad_alloc.
// Ejemplo de fuga\nvoid leak() {\n int* ptr = new int(42);\n // Olvidamos delete ptr;\n}\n\n// Enfoque correcto\nvoid noLeak() {\n std::unique_ptr<int> ptr = std::make_unique<int>(42);\n // La memoria se liberará automáticamente al salir del ámbito\n}\n\nCómo protegerse: Utilice punteros inteligentes (std::unique_ptr, std::shared_ptr). En última instancia, use RAII (Resource Acquisition Is Initialization).
3. Punteros colgantes (Dangling Pointers)
\nUn puntero hace referencia a memoria ya liberada. Desreferenciar dicho puntero es un comportamiento indefinido (UB). Esto puede provocar un bloqueo del programa o, peor aún, la ejecución de código malicioso.
\n\nint* dangling() {\n int x = 10;\n return &x; // Error: ¡devolvemos la dirección de una variable local!\n}\n\n// Correcto: asignar memoria en el heap\nint* safe() {\n int* ptr = new int(10);\n return ptr;\n}\n\nCómo protegerse: Después de delete, asigne siempre nullptr al puntero. Utilice punteros inteligentes.
4. Desbordamiento de enteros (Integer Overflow)
\nCuando el resultado de una operación aritmética supera el rango del tipo de datos, se produce un desbordamiento. Esto puede tener consecuencias inesperadas, como la asignación de un búfer demasiado pequeño.
\n\nvoid check_size(size_t user_size) {\n // Vulnerabilidad: si user_size es muy grande, user_size + 1 puede volverse pequeño\n char* buffer = new char[user_size + 1];\n // ...\n}\n\n// Protección\nvoid safe_check(size_t user_size) {\n if (user_size > std::numeric_limits<size_t>::max() - 1) {\n throw std::overflow_error("Tamaño demasiado grande");\n }\n char* buffer = new char[user_size + 1];\n}\n\nCómo protegerse: Verifique los límites antes de las operaciones aritméticas. Utilice __builtin_add_overflow (GCC/Clang) o bibliotecas para aritmética segura.
5. Punteros nulos (Null Pointer Dereference)
\nDesreferenciar un puntero nulo