Los 5 mejores consejos de seguridad para C++
DeveloperSteve Coochin
19 de julio de 2022
0 minutos de lecturaC++ se ha convertido en una pieza clave de la industria tecnológica moderna. Se ha utilizado para múltiples propósitos, como aplicaciones de escritorio y de servidor, videojuegos, realidad virtual, firmware para el Internet de las cosas e incluso como base de muchos lenguajes de programación modernos.
Desde su lanzamiento inicial en 1985 como extensión del lenguaje de programación C, C++ se diseñó con un enfoque en la programación de sistemas y el desarrollo de recursos integrados.
Como ocurre con cualquier desarrollo de uso generalizado o basado en usuarios, es importante integrar medidas de seguridad en toda la aplicación. Veamos algunos consejos de seguridad que debes tener en cuenta al desarrollar con C++.
1. Validación y sanitización de entradas en aplicaciones C++
La gestión de las entradas de los usuarios es fundamental para proteger una aplicación de intrusiones no deseadas. C++ se ha usado ampliamente a lo largo de los años porque permite manipular la memoria orientada a objetos. Aunque esto resulta especialmente útil para el desarrollo de aplicaciones, también puede ser aprovechado por actores maliciosos como vector de ataque.
Un método consiste en usar la función std:cin para gestionar variables. Se puede aplicar de varias maneras, por ejemplo:
En esta función, cin.clear() borra los indicadores de estado de error y cin.ignore() elimina todos los caracteres que quedan en el flujo hasta el carácter de nueva línea. Es importante gestionar esos mensajes de error para que no aparezcan en la salida que ve el usuario final. Los mensajes de error y alerta pueden revelar vectores de ataque no deseados.
2. Desbordamientos o subdesbordamientos de búfer
Al gestionar cualquier entrada de usuario en C++, es importante verificar su longitud, rango, formato y tipo. El enfoque más granular de C++ para interactuar con el sistema operativo subyacente implica que, si se inyecta una gran cantidad de datos en el búfer de entrada de una función, puede producirse un ataque de desbordamiento.
Se produce un desbordamiento aritmético cuando el resultado de una operación supera los valores que puede representar el tipo de la expresión. Por ejemplo:
La función anterior mostrará 0 como respuesta porque x es un entero sin signo que puede representar valores entre 0 y 65535. Al exceder ese rango, el valor no puede representarse con unit16_t, lo que provoca un desbordamiento.
Una forma de gestionar los desbordamientos y subdesbordamientos es establecer una captura al procesar un tipo de variable entre sus valores mínimo y máximo. De este modo, se detecta cualquier valor que quede por encima o por debajo de los límites del rango. El uso de plantillas define ese rango y, a su vez, permite crear una captura reutilizable para todos los casos.
Es importante evitar el comportamiento indefinido al usar C++. La mayoría de los compiladores te advertirán sobre este tipo de comportamiento al compilar la aplicación. Por ejemplo, GCC y Clang tienen la -fsanitize=undefined flag.
Además, ten cuidado al usar funciones como strcpy() y strcat() en C++. Aunque son útiles para gestionar variables, pueden provocar desbordamientos o subdesbordamientos si el valor que se almacena supera la capacidad del tipo de almacenamiento.
3. Las operaciones incorrectas con el sistema de archivos pueden provocar vulnerabilidades de recorrido de rutas
La gestión inadecuada de archivos puede provocar vulnerabilidades de recorrido de rutas. Esto es peligroso en cualquier framework, pero especialmente en un sistema operativo y con un lenguaje granular como C++. En C++, los archivos se gestionan como flujos y como una abstracción que representa la entrada y salida de datos. Según la estructura del código, un flujo puede representarse como una secuencia de caracteres de longitud indefinida.
C++ usa varias técnicas de gestión de archivos, entre ellas ifstream(), ofstream() , y fstream(). Estas funciones derivan de los grupos de clases fstreambase e iostream y están diseñadas para gestionar los archivos fisk que se declaran en fstream.
Las diferencias entre las distintas clases de gestión de archivos son las siguientes:
ofstream: Esta clase representa el flujo de salida y se usa para crear archivos y escribir información en ellos.ifstream: Esta clase representa el flujo de entrada de archivos y se usa para leer información de ellos.fstream: Esta clase se puede usar tanto para leer como para escribir en archivos.
Es importante gestionar los flujos de archivos de forma segura. Primero, usa new_file.open() para iniciar un archivo nuevo y, luego, escribe el flujo en el archivo. Por último, cierra el archivo cuando hayas terminado e incluye una instrucción catch para gestionar los errores que puedan producirse durante la transferencia.
Por ejemplo:
4. Limita el uso de system() y de las llamadas de ejecución de procesos para evitar la inyección de comandos
Al igual que en muchos otros lenguajes, la interacción directa con el sistema mediante funciones de código debe abordarse con mucha cautela. En C++, se deben evitar funciones como system() o utilizarlas con cuidado. Estas funciones de sistema varían según el chipset y el hardware en los que se ejecute la aplicación.
También es importante asegurarse de que la aplicación se instale y ejecute de forma que permita funciones ejecutables adicionales, como exec(), execp() , y CreateProcess(). Sin embargo, también hay que usar estas funciones con cautela, sobre todo porque pueden dar acceso al sistema operativo.
Ten en cuenta que las aplicaciones basadas en C++ tienen un acceso mucho más granular a información crítica del sistema, lo que puede exponer más actividades maliciosas y rutas vulnerables.
5. Analiza las bibliotecas del proyecto con una herramienta integral de análisis estático de código
Dar seguimiento a las rutas potencialmente vulnerables de las bibliotecas y dependencias de tu proyecto es fundamental para mantener la seguridad. Las rutas vulnerables pueden permitir comportamientos no deseados por parte de actores maliciosos, especialmente en proyectos de C++ que se instalan en distintos sistemas y niveles de arquitectura.
El análisis estático de código, o SCA, es útil porque puede alertarte sobre posibles vulnerabilidades mientras desarrollas una aplicación y notificarte cuando se detecta una vulnerabilidad en el proyecto que se va a implementar.
Cómo empezar a analizar código C++ con Snyk SCA
Para empezar a usar Snyk, tendrás que instalar la Snyk CLI en tu entorno de desarrollo. Hay varias formas de instalar la CLI, como npm, Yarn y los ejecutables independientes.
El siguiente paso es conectar la CLI con tu cuenta de Snyk. Si todavía no tienes una cuenta de Snyk, registrarte es rápido y fácil. Ve a la página de registro y sigue los pasos para comenzar.
Una vez que hayas instalado la CLI e iniciado sesión en tu cuenta de Snyk, ejecuta snyk auth desde la línea de comandos para terminar de conectar tu cuenta. Se generará un enlace de autenticación y se abrirá automáticamente el navegador en la página de autenticación. Haz clic en Authenticate y podrás empezar a usar Snyk CLI.

Por último, ve al directorio raíz del proyecto que quieres analizar y ejecuta snyk test –unmanaged. El proceso de análisis identificará la carpeta en la raíz del proyecto que contiene las bibliotecas de código abierto y ejecutará el análisis.

Cuando termine, verás todos los resultados y podrás investigarlos más a fondo. Una de las cosas que más me gusta es que puedes profundizar en los problemas identificados mediante los enlaces del análisis para entender mejor cada vulnerabilidad.

Regístrate ahora y empieza a analizar
Como desarrolladores, profesionales de DevOps y personas del sector tecnológico, es importante priorizar la seguridad y hacer todo lo posible para proteger las aplicaciones que creamos. Integrar la seguridad en el desarrollo de aplicaciones ayuda a proteger a los usuarios finales y mantenerlos a ellos y sus datos a salvo.
Empieza con los retos de Capture the Flag
Aprende a resolver retos de Capture the Flag viendo nuestro taller virtual de nivel básico a pedido.
