Proceso de análisis de Cranelift para ofrecer espacios aislados seguros en Compute
En 2019 ayudamos a fundar la Bytecode Alliance con el objetivo de crear una comunidad de código abierto destinada a colaborar en bases y herramientas de compilador basadas en WebAssembly compatibles con varias plataformas. WebAssembly constituye los cimientos de Compute, nuestro entorno informático sin servidores, y seguimos invirtiendo en su futuro porque los programas de WebAssembly ofrecen garantías de seguridad más robustas que el código nativo.
Hoy queremos presentar un importante componente de seguridad de nuestra implementación de WebAssembly: Cranelift, un generador de código de última generación (y de código abierto) que aporta seguridad de espacio aislado.
En una entrada reciente podrás descubrir cómo detectamos y respondimos a un problema concreto de seguridad en Cranelift antes de alguien lo pudiera aprovechar. En esa entrada hablamos de las formas en que podemos trabajar de forma proactiva y continua para detectar problemas de este tipo, a partir de evaluaciones específicas de seguridad y durante nuestros procesos normales de desarrollo. Estamos convencidos de que la seguridad debe contemplarse a lo largo de todo el ciclo de desarrollo, usando todo un conjunto de técnicas y herramientas.
En esta entrada nos centraremos en dos de esas técnicas: el denominado «fuzzing», una metodología de pruebas de vulnerabilidad muy conocida, y la evaluación completa de seguridad en el nuevo backend de generación de código de Cranelift que realizamos recientemente. Conozcamos más sobre Cranelift y sobre cómo trabajamos para mejorarlo.
¿Qué es Cranelift?
Cranelift es un generador de código nativo que toma una descripción de un programa de forma independiente a su arquitectura y genera código rápido nativo para el procesador concreto con el que está trabajando. Actúa de pilar fundamental en el trabajo de la Bytecode Alliance para establecer bases para un software eficiente y seguro.
Pese a estar pensado originalmente como un elemento añadido a los backends del compilador de Firefox, el equipo de Cranelift decidió en su momento crear un motor que permitiera su uso más allá. La Bytecode Alliance sigue contribuyendo a crear una comunidad de usuarios y colaboradores en torno a Cranelift, incluidos los motores de WebAssembly elaborados a partir de Cranelift, como los tiempos de ejecución Lucet y Wasmtime. Junto a la Bytecode Alliance, trabajamos de manera activa para mejorar su rendimiento y seguridad, lo que beneficia a todos los usuarios de este proyecto.
Espacio aislado seguro y protegido para Compute
Así pues, ¿cómo funciona? Al usar Cranelift, los motores de WebAssembly pueden tomar un programa de WebAssembly de poca confianza y compilarlo en un código máquina de espacio aislado seguro. Al traducir Cranelift el código original de WebAssembly a código máquina, inserta comprobaciones de seguridad y protección que se ejecutan al mismo tiempo que el código. Por esa razón, resulta esencial verificar que el código máquina es una traducción correcta del original de WebAssembly mediante comprobaciones insertadas del espacio aislado. Como publicamos en una entrada reciente, existen salvaguardas adicionales si fallan dichas comprobaciones, pero estamos trabajando duro para no tener que recurrir a las mismas.
Este enfoque permite a Compute ejecutar servicios de forma eficiente con tiempos de inicio muy rápidos. Esto es posible porque —en vez de depender de procesos del sistema operativo (como los navegadores actuales o algunas plataformas de alojamiento compartido), de contenedores (como muchos productos en la nube sin servidores) o de máquinas virtuales enteras (como el alojamiento tradicional en la nube no gestionado) para separar los distintos servicios y peticiones— usamos sencillas comprobaciones del espacio aislado insertadas en código nativo. De esta forma, el código nativo de diferentes servicios puede coexistir de forma segura en un único proceso del SO.
Nos esforzamos para garantizar que Cranelift compila código de forma que conserve todas las comprobaciones de seguridad del espacio aislado, y aun así sea lo suficientemente rápido para que nuestros clientes lo pongan al frente de todas y cada una de las peticiones web dirigidas a sus sitios.
El proceso de prueba: fuzzing y auditorías de seguridad
Hemos determinado que la correcta compilación del código es fundamental para la seguridad de los servicios de los que dependen nuestros clientes, y que cada error de compilación representa un grave problema. ¿Cómo garantizamos que el compilador cumpla las expectativas?
La primera técnica que usamos se llama «fuzzing». Es un método de prueba que envía muchas entradas aleatorias diferentes a un programa en un intento de alterarlo. Se trata de una forma muy potente de probar programas, porque las entradas aleatorias tocarán casos marginales que los probadores pueden no contemplar al escribir pruebas manuales. Un buen marco de trabajo de fuzzing deberá observar cómo responde el programa a estas entradas y encontrar vías para llegar a todos los recovecos del programa, lo que se conoce como «fuzzing dirigido por feedback». Este enfoque sistemático ofrece una capacidad sorprendente para descubrir problemas que se pasan por alto.
Mozilla ya lo ha aplicado rigurosamente en Cranelift, pero lo haremos igualmente por nuestra cuenta. En la actualidad, el compilador realiza un fuzzing continuo a través de la participación del proyecto Wasmtime en el proyecto de fuzzing de la infraestructura OSS-Fuzz de Google. De hecho, se trató del primer proyecto escrito principalmente en Rust que aceptó el programa OSS-Fuzz. En nuestro afán por mejorar las pruebas de Cranelift basadas en fuzzing, hemos añadido código para comparar la ejecución basada en Cranelift con un intérprete de WebAssembly, comparar múltiples backends de Cranelift entre sí y demostrar simbólicamente una correcta asignación de registros, entre otras cosas. La Bytecode Alliance manifestó que el fuzzing es de gran utilidad a la hora de encontrar problemas de compilador y de tiempo de ejecución, ¡y estamos de acuerdo!
También colaboramos con la compañía de seguridad Atredis Partners en una evaluación específica de seguridad, que incluyó tanto análisis estático manual como pruebas dinámicas. Nos centramos en la asignación de registros, codificación de instrucciones y optimizaciones, y también en cualquier superficie de ataque potencial en la que el tiempo de ejecución interactuase con el código generado. La evaluación incluyó un estudio de herramientas de ejecución de fuzzing existentes para garantizar una buena cobertura de código. La conclusión fue que el tiempo de ejecución de Lucet y el nuevo backend de Cranelift estaban bien diseñados e implementados y que hacían un uso correcto de las técnicas de prueba, incluido el fuzzing.
En resumen
Seguimos realizando minuciosas pruebas y verificaciones a medida que desarrollamos el compilador y sus integraciones en sistemas de más envergadura (como Lucet), a las que se suman los procesos, tanto humanos como técnicos, establecidos para dar respuesta a los problemas cuando se producen. Por todo ello tenemos la seguridad de que Cranelift puede convertirse en un componente fiable, rápido y seguro de nuestro entorno informático sin servidores. Dadas las posibilidades que ofrece Cranelift en lo que se refiere a espacios seguros en Compute, seguiremos invirtiendo esfuerzos en su velocidad y calidad, tanto para nuestros clientes como para la comunidad de código abierto en general.