Durante los últimos años, parece que los ingenieros de software de todo el mundo no se cansan de usar Rust para programar. Este lenguaje de programación de sistemas relativamente nuevo, producido por Mozilla, ha conquistado los corazones de la comunidad de Stack Overflow y, como cohorte, es muy poco probable que sufra a los tontos, cuando votan por algo, el»lenguaje de programación más querido«cinco años seguidos, es hora de que todos nos sentemos y tomemos nota.
El lenguaje de programación Rust incorpora elementos conocidos y funcionales de los lenguajes de uso común, siguiendo una filosofía diferente que elimina la complejidad, al tiempo que introduce rendimiento y seguridad. Es una curva de aprendizaje y muchos desarrolladores no tienen la oportunidad de jugar mucho con ella - solo el 5,1% de los encuestados en Stack Overflow lo usaba comúnmente. Sin embargo, aparte de eso, no se puede negar que es un lenguaje interesante y con mucha más potencia de seguridad que sus predecesores, como C y C++. La adopción masiva va a requerir algunos cambios, tanto de comportamiento como tecnológicos... pero, en este momento, sigue captando la atención de los desarrolladores a nivel teórico.
... pero espera, necesitamos arrojar luz sobre una cosa más: es importante tener en cuenta que Rust es un lenguaje de programación que prioriza la seguridad de la memoria y la erradicación de los errores de seguridad relacionados con problemas comunes de administración de la memoria. Estos problemas son graves (y, sin duda, causan más de un par de problemas al equipo de AppSec), pero no son los únicos desafíos de codificación segura a los que nos enfrentamos.
¿Qué es lo que previene la oxidación exactamente? ¿Y en qué aspectos del panorama de la seguridad aún estamos expuestos? Analicemos el último unicornio de la programación:
La nueva frontera de la programación de sistemas modernos y seguros para la memoria
El equipo de investigación y desarrollo de Mozilla ha trabajado en algunos proyectos increíbles, e invertir en la programación de Rust como pionero del código abierto no es una excepción. Sus vídeo introductorio proporciona una idea de su ética, con el tema clave muy claro: el enfoque actual de la seguridad del software es defectuoso y Rust está diseñado para resolver gran parte de ese problema.
Parece demasiado simplista, sobre todo porque nos enfrentamos a enormes filtraciones de datos cada dos días, al igual que el reciente y terrible error denunciado por easyJet. Millones de registros de datos se ven comprometidos con frecuencia, casi siempre debido a una vulnerabilidad de una aplicación web, mala configuración de seguridad, o ataque de suplantación de identidad, y lenguajes como C++ existen desde hace décadas. Sin embargo, este tiempo no ha sido suficiente para que los desarrolladores los dominen hasta el punto de implementar las mejores prácticas de codificación segura. ¿Por qué debería ser diferente Rust? Ya han aparecido nuevos lenguajes, y no es que hayan encontrado una forma de erradicar las vulnerabilidades más comunes o de garantizar que cualquier código escrito sea mágicamente perfecto cuando se compila.
Por muy simple que sea el concepto, a veces son las respuestas simples las que superan las preguntas complejas. Rust es, en todo el sentido de la palabra, una revolución en la programación de sistemas con memoria segura que cumple sus promesas de muchas maneras... y, sin duda, salva el trabajo de los desarrolladores, que son propensos a introducir errores que pueden causar grandes problemas si no se detectan. Java, C, C++ e incluso lenguajes más nuevos, como Kotlin y Golang, siguen siendo bastante implacables para los desarrolladores que no se preocupan por la seguridad. Con ellos, no hay advertencias ni señales particulares de que la increíble función que se acaba de compilar tenga a un gremlin de la seguridad escondido bajo el capó.
Entonces, profundicemos más:
¿Qué hace que Rust sea tan seguro?
Por lo general, un desarrollador tiene el objetivo principal de crear funciones, garantizar que sean funcionales y fáciles de usar, tal vez incluso motivos de orgullo de los que estarían encantados de mostrar en su currículum. Es totalmente normal que un desarrollador cree un software excelente, lo publique y pase al siguiente gran proyecto. En este punto, los equipos de seguridad comprueban si hay vulnerabilidades y, si las encuentran, su aplicación «finalizada» podría ser enviada a su equipo para corregirla. El problema puede ser simple o puede estar fuera del alcance razonable para que un desarrollador lo solucione.
El problema es que, a primera vista, los errores de seguridad no eran evidentes en absoluto y, si el escaneo, las pruebas y la revisión manual del código no los detectan, un atacante podría aprovechar esa pequeña oportunidad para aprovechar el error.
Ahora, Rust busca evitar que muchas vulnerabilidades lleguen al código desde el principio: simplemente no se compilará si hay errores de sintaxis u otros errores de seguridad de la memoria que causan problemas de producción a lo largo del SDLC. Se trata de una programación diseñada para proteger la memoria, lo que garantiza que no haya acceso a memoria no válida (sin importar cómo se ejecute el software). ¿Y con El 70% de todos los errores de seguridad son el resultado de problemas relacionados con la administración de la memoria, es una gran hazaña.
El óxido marcará y evitará:
- Desbordamiento de búfer
- Úselo después de que esté gratis
- Doble gratis
- Desreferencia de puntero nulo
- Uso de memoria no inicializada
Si comparamos un fragmento de código de Rust con C++, se hará evidente que uno es seguro por defecto. Mira este ejemplo de un error de desbordamiento de búfer:
#include<iostream></iostream>
#include<string.h></string.h>
int main (void) {
char a [3] = «12";
char b [4] = «123";
strcpy (a, b);//desbordamiento de búfer cuando len of b es mayor que a
std: :cout << a << «;" << b << std: :endl;
}
대.
pub fan main () {
deje que sea: [char; 2] = [1, 2];
sea b: [char; 3] = [1, 2, 3];
a.copiar_desde_rebanada (&b);
}
Rust lanza una advertencia de seguridad y entra en pánico al llegar a la función copy_from_slice en tiempo de ejecución para evitar el desbordamiento del búfer, pero no en tiempo de compilación.
En ese sentido, es en gran medida uno de los lenguajes de «inicio a la izquierda». Resaltará los errores y forzará a los desarrolladores a aprender a escribir código para evitar introducir errores de seguridad relacionados con la memoria, por lo que cumplir con los plazos depende de que el programador preste atención, corrija y se mantenga fiel a la ruta de entrega.
El enfoque de este lenguaje parece simple, pero habría sido una hazaña increíble hacerlo funcionar con esta poderosa lógica, y lo hace con el ejemplo. Rust es, desde el punto de vista de la seguridad, un gran avance... si tan solo lo usaran más personas. Empresas como Dropbox son pioneras en su uso a gran escala corporativa, y es fantástico comprobarlo. Sin embargo, hay más consideraciones antes de llegar a la conclusión de que lo único que nos impide lograr un futuro más seguro es una cuestión de adopción.
El ajuste de cuentas de Rust.
Hay un par de problemas pequeños (bueno, grandes), a saber, que la programación en Rust tiene más flexibilidad para introducir errores de lo que parece. Lo hará no corrija las 10 principales vulnerabilidades más importantes de OWASP que siguen provocando brechas, retrasos y una cultura general de técnicas de codificación inseguras. También hay una especie de dinámica entre ángeles y demonios, o, como se le conoce más ampliamente: Óxido seguro contra óxido inseguro.
Como se explica en el documentación oficial, Safe Rust es la versión «verdadera» de Rust, y Unsafe Rust incluye funciones que se consideran «definitivamente no seguras», aunque a veces son necesarias, por ejemplo, si se requiere la integración con algo en otro idioma. Sin embargo, incluso con Unsafe Rust, la lista de funcionalidades adicionales sigue siendo limitada. En Unsafe Rust, es posible hacer lo siguiente dentro de bloques inseguros:
- Desreferenciar punteros sin procesar
- Llame a funciones no seguras (incluidas las funciones de C, los elementos intrínsecos del compilador y el asignador sin procesar)
- Implemente rasgos inseguros
- Estática mutada
- Acceda a los campos de los sindicatos.
Incluso en el llamado modo «inseguro», uno de los superpoderes de la programación de Rust sigue funcionando: el «comprobador de préstamos». Por lo general, este método evita problemas de memoria, colisiones en los cálculos paralelos y muchos otros errores mediante el análisis de código estático. Este análisis seguirá comprobando si un bloque no es seguro. Solo que se necesita mucho más trabajo para escribir construcciones no seguras sin que el compilador intervenga como guía en determinadas situaciones.
Esto no parece ser un gran problema para la mayoría de los desarrolladores experimentados (al fin y al cabo, se nos conoce por hacer pequeños retoques para sacar el máximo provecho de nuestras aplicaciones y abrir algunas funciones interesantes), pero puede abrir un agujero negro que puede provocar graves errores de configuración y vulnerabilidades de seguridad: un comportamiento indefinido. La programación en Rust (incluso cuando se usa de forma insegura) bloquea bastante bien las posibilidades de vulnerabilidades en comparación con C o C++, pero invocar un comportamiento indefinido puede ser un riesgo.
¿Es este el fin de la confianza en la codificación segura dirigida por los desarrolladores?
¿Recuerdas cuando dije que Rust tiene componentes de lenguajes conocidos? Una de las principales vulnerabilidades de seguridad de Rust es que, bueno, tiene componentes de lenguajes muy conocidos, a saber, C.
Rust sigue siendo un «lenguaje de programación seguro», pero una vez más, la introducción de un usuario es donde las cosas pueden despegarse. El desarrollador aún puede modificarlo para que funcione sin detectar errores (una propuesta atractiva, ya que esto desbloquea más capacidades) y, en esencia, incluso en un estado seguro, los desarrolladores pueden seguir siendo tan «inseguros» como quieran, ya que tienen un nivel de orientación y protección antes de que las cosas salgan bien.
Y los dos escenarios anteriores se vuelven más peligrosos a medida que profundizamos, ya que los resultados de Rust son similares a los de las herramientas de escaneo, del mismo modo que no existe ninguna herramienta SAST/DAST/RAST/IAST del ejército suizo que analice todas las vulnerabilidades, todos los vectores de ataque y todos los problemas, Rust tampoco. Incluso con Rust algunas vulnerabilidades aún se pueden introducir con bastante facilidad.
El riesgo de comportamiento indefinido al ejecutar Unsafe Rust puede generar problemas de desbordamiento de enteros, mientras que, en general, ni siquiera las configuraciones seguras evitarán el error humano en las configuraciones incorrectas de seguridad. lógica empresarial, o usar componentes con vulnerabilidades conocidas. Estos problemas siguen representando una amenaza muy real si no se solucionan, y en un entorno que se considera «seguro», como el verdadero Rust, puede incluso provocar cierto grado de autocomplacencia si un programador cree que todos los problemas importantes se solucionarán de todos modos.
He descubierto que Rust no es diferente a un mentor de programación: un ingeniero sénior que se ha tomado el tiempo de sentarse junto a un programador con menos experiencia, revisar su trabajo y mostrarle posibles errores, señalar las eficiencias y, en algunos casos, asegurarse de que no se compila hasta que esté bien. Sin embargo, es mucho mejor para los programadores de Rust aprender la teoría y comprometerse ellos mismos con las mejores prácticas, ya que ese mentor podría simplemente cortar los hilos del delantal y no querrás quedarte colgado.
¿Estás listo para encontrar y corregir las vulnerabilidades comunes de Rust ahora mismo? Juega el desafío.