Más allá de C

Ecosistema de F5 | 07 de febrero de 2023

Durante décadas, C y sus descendientes directos han sido los lenguajes de programación preferidos para la programación de sistemas. A principios de la década de 1970, cuando se desarrolló C, supuso un importante avance con respecto al lenguaje ensamblador. Cincuenta años después, podemos hacerlo mejor.

En 1970, la seguridad informática no era una preocupación para la mayoría de la gente, y siguió sin serlo durante muchos años. La primera gran vulnerabilidad de seguridad en internet se produjo mucho después, en 1988. Se conoció como el gusano de internet y aprovechaba un desbordamiento de búfer, por el que se accedía a un índice de una matriz en memoria que estaba fuera de sus límites.

En la familia de lenguajes C, que incluye C++, no se comprueba que los accesos a los arrays estén dentro de sus límites. Es responsabilidad del programador asegurarse de que se accede correctamente al array. Por tanto, los errores de desbordamiento de búfer son habituales. Peor aún, es fácil provocar deliberadamente un desbordamiento de búfer y acceder así a zonas de memoria a las que no se debería tener acceso.

El desbordamiento de búfer es solo un ejemplo de inseguridad de memoria. Otros ejemplos relacionados son la aritmética de punteros y los punteros colgantes (también conocidos como errores de uso después de liberar la memoria). Hoy en día, disponemos de muchos lenguajes de programación que emplean diversas técnicas para garantizar que cualquier programa escrito en ellos esté libre de problemas de seguridad de memoria. La familia de lenguajes C no ofrece tal garantía; la seguridad de memoria nunca fue un objetivo de diseño de estos lenguajes.

Alrededor del 70 % de las vulnerabilidades de seguridad se deben a vulneraciones de la seguridad de la memoria. Esta afirmación está respaldada por una cantidad abrumadora de datos. Los problemas de seguridad de la memoria representan:

  • El 67 % de las vulnerabilidades explotadas, divulgadas y detectadas en entornos reales (según la estimación de Google Project Zero de 2021).
  • El 90 % de las vulnerabilidades de Android (según Google). Aunque esta cifra ha disminuido recientemente gracias al uso de Rust, el 89 % de las vulnerabilidades explotables de forma remota siguen estando relacionadas con la seguridad de la memoria.
  • El 70 % de las vulnerabilidades de Microsoft (según Microsoft).
  • La mayoría de las correcciones recientes de vulnerabilidades de Apple (en Catalina, Big Sur, Monterey, Safari, iOS, tvOS, watchOS).

En julio de 2022, 5/6 de las vulnerabilidades corregidas en Chrome 103.0.5060.134 eran problemas de seguridad de memoria. Está claro que eliminar los errores de seguridad de memoria sería de enorme ayuda. La industria sabe desde hace muchos años cómo hacerlo: utilizando lenguajes de programación con seguridad de memoria. Históricamente, el problema siempre ha sido el coste asociado en términos de rendimiento. Dada la importancia de la seguridad de memoria, se ha trabajado en nuevas estrategias para conseguirla sin la sobrecarga tradicional. Hoy sabemos cómo eliminar los errores de seguridad de memoria con un coste mínimo o nulo en tiempo de ejecución. Lenguajes como Rust incorporan innovaciones en el diseño de sistemas de tipos para garantizar la seguridad de memoria sin necesidad de un costoso soporte en tiempo de ejecución.

Esto lleva a una conclusión ineludible: hay que dejar de escribir nuevo código de sistemas en C/C++ (o, en términos más generales, en lenguajes que no garantizan la seguridad de la memoria).

Esto no es un llamamiento a reescribir de forma masiva e indiscriminada el código existente. Sustituir el software existente es costoso y no está exento de riesgos. Sin embargo, el sector debe dejar de agravar el problema añadiendo más código que no garantice la seguridad de la memoria a las bases de código existentes. En cuanto al código existente, debe darse prioridad a la reescritura de los componentes más sensibles: los responsables de validar o procesar entradas de usuarios que no sean de confianza, los que se ejecutan en un contexto con privilegios, los que se ejecutan fuera de un entorno aislado, etc.

Esta postura, aunque ampliamente compartida, sigue siendo controvertida para algunos. A continuación se exponen algunos de los argumentos habituales a favor del statu quo y nuestras respuestas a ellos.

  • Los errores no son exclusivos de los lenguajes de programación.
    • La mayoría de las vulnerabilidades se deben a errores de seguridad de memoria. En lenguajes con seguridad de memoria, como Rust, no pueden producirse este tipo de errores. Por lo tanto, usar un lenguaje con seguridad de memoria evitará que la mayoría de las vulnerabilidades lleguen a crearse.
  • Las revisiones de código detectarán errores.
    • Los estudios demuestran que, aunque las revisiones de código pueden mejorar las cosas, a menudo pasan por alto muchos problemas; desde luego, no los detectan todos.
  • Los módulos no seguros hacen que todo esto carezca de sentido.
    • El código no seguro se delimita explícitamente en secciones de código muy pequeñas, de modo que los recursos puedan centrarse en validarlo.
  • La implementación de un lenguaje con seguridad de memoria podría contener errores.
    • En efecto, pero las probabilidades son mucho menores. Los compiladores son relativamente pequeños y se someten a pruebas rigurosas. Además, corregir un compilador corregirá automáticamente todos los programas compilados con él, a diferencia de corregir un error en un único programa.
  • Solo los programadores novatos o incompetentes cometen estos errores.
    • Al contrario, los datos anteriores proceden de grandes proyectos de sistemas, como el navegador Chrome y los sistemas operativos Windows y macOS. Estos proyectos cuentan con algunos de los desarrolladores más competentes y experimentados del sector y, aun así, siguen presentando problemas de seguridad de la memoria.
  • Estos problemas pueden evitarse siguiendo las prácticas recomendadas.
    • Véase más arriba. Los equipos mencionados siguen prácticas muy rigurosas y utilizan las mejores herramientas disponibles.
  • No es práctico reescribir todo el código.
    • Nadie propone reescribir por completo todo el código. En su lugar, se recomienda adoptar un enfoque centrado en los elementos clave (privilegios elevados, gran superficie de ataque y elementos esenciales para las garantías de seguridad), así como escribir el código nuevo en un lenguaje con seguridad de memoria.
  • El rendimiento es insuficiente.
    • El rendimiento de Rust es aproximadamente equiparable al de C/C++. Las pequeñas diferencias no justifican los riesgos de seguridad. También hay situaciones en las que los programas escritos en Rust pueden ser más rápidos.
  • Los lenguajes de programación de sistemas con seguridad de memoria, como Rust, son demasiado nuevos.
  • Existen otras soluciones, como el análisis estático, las pruebas de fuzzing y el aislamiento en entornos sandbox.

Consulte Cuantificación de la falta de seguridad de la memoria y las reacciones al respecto para obtener un análisis detallado de los puntos anteriores.

A pesar de las objeciones, las reformas necesarias están cobrando impulso. Por ejemplo, se están realizando grandes inversiones en la Open Source Software Security Foundation (respaldada por la Linux Foundation). La seguridad de la memoria se ha abordado en informes del Senado de Estados Unidos y también por parte de la NSA. Consumer Reports también trabaja para identificar los distintos incentivos que podrían acelerar este movimiento mediante una serie de recomendaciones dirigidas a empresas y organismos estatales.

En resumen:

La importancia de la informática para la sociedad ha crecido enormemente durante los últimos cincuenta años. El panorama de amenazas para nuestra infraestructura informática también ha cambiado radicalmente en las últimas décadas. Sin embargo, los lenguajes de programación que utilizamos para crear nuestros sistemas informáticos no han evolucionado en consonancia. La falta de seguridad de la memoria es, por sí sola, la principal fuente de vulnerabilidades de seguridad en el software. Este problema no es exclusivo de ningún tipo concreto de software: allí donde se utilizan lenguajes sin seguridad de memoria, abundan los problemas relacionados con ella. Y, atendiendo a las cifras, ninguna otra clase de vulnerabilidad se le acerca.

Ahora disponemos de los medios para abordar este problema creciente y es fundamental que, como sector, lo hagamos. Afortunadamente, cada vez hay más conciencia de la situación, pero el problema es urgente y no hay tiempo que perder.

Compartir

Sobre los autores

Artículos relacionados del blog

Proteger los endpoints de las API dificulta la seguridad de las API
Ecosistema de F5 | 30/01/2024

Proteger los endpoints de las API dificulta la seguridad de las API

Descubre las complejidades de la seguridad de las API, los retos a los que se enfrentan las organizaciones, la evolución de los ataques contra endpoints de API y la necesidad de políticas de seguridad adaptadas.

¿Qué es el sesgo de la IA y por qué es importante para TI?
Ecosistema de F5 | 22/02/2023

¿Qué es el sesgo de la IA y por qué es importante para TI?

Descubre los fundamentos del aprendizaje de la IA. Aprende cómo pueden surgir sesgos en la IA y qué riesgos plantean para las TI y la transformación digital.

Redefiniendo la ciberseguridad en el extremo de la nube distribuida con IA y telemetría en tiempo real
Ecosistema de F5 | 01/11/2021

Redefiniendo la ciberseguridad en el extremo de la nube distribuida con IA y telemetría en tiempo real

La transformación digital y la adopción de tecnologías en la nube están provocando cambios en las arquitecturas de las aplicaciones. Estos cambios introducen nuevas superficies de ataque, que son el objetivo de métodos de ataque avanzados, altamente organizados y con motivaciones económicas. Mediante el uso del framework Morpheus de NVIDIA, F5 está investigando técnicas para detectar estas amenazas avanzadas con telemetría en tiempo real y análisis basados en IA.

El papel de la nube en las aplicaciones nativas del edge
Ecosistema de F5 | 14/06/2021

El papel de la nube en las aplicaciones nativas del edge

No debería sorprender que, al igual que la aparición de la nube dio lugar a las aplicaciones nativas de la nube, el edge esté impulsando un conjunto de aplicaciones nativas del edge. Sin embargo, estas aplicaciones no residirán únicamente en el edge. Al mismo tiempo, están surgiendo nuevos patrones arquitectónicos que aprovechan las aplicaciones alojadas en la nube y en el centro de datos.

La creciente diversidad de ubicaciones y usuarios está impulsando a las empresas hacia el edge
Ecosistema de F5 | 03/05/2021

La creciente diversidad de ubicaciones y usuarios está impulsando a las empresas hacia el edge

En el contexto del ámbito de las aplicaciones, el término «usuario» siempre se ha referido a la entidad que interactúa con una aplicación. Del mismo modo que los cambios en las arquitecturas de las aplicaciones impulsan cambios en las tecnologías que permiten ofrecerlas y protegerlas, los cambios en la definición de usuario han provocado cambios en los lugares donde se despliegan las aplicaciones.

El edge está impulsando un renacimiento de las infraestructuras
Ecosistema de F5 | 26/10/2020

El edge está impulsando un renacimiento de las infraestructuras

A medida que las organizaciones aumentan la generación de datos y tratan de extraer valor empresarial de ellos, sin duda recurrirán a la analítica y la automatización basadas en la IA y el aprendizaje automático. Precisamente este tipo de cargas de trabajo son las que más se beneficiarían de una infraestructura optimizada y, sin embargo, hoy en día son las que menos probabilidades tienen de poder aprovecharla.