Emily Ratliff, directora ejecutiva de seguridad de infraestructura de la Core Infrastructure Initiative de The Linux Foundation, profundiza sobre el uso del fuzzing en proyectos Open Source.
Una de las mejores prácticas para el desarrollo seguro es el análisis dinámico. Entre las diversas técnicas de análisis dinámico, el fuzzing[1] ha sido muy popular desde su invención y se han desarrollado una multitud de herramientas de fuzzing de complejidad variable.
En el contexto de hacer fuzzing a una sola aplicación, detectar y corregir fallos en un sistema puede ser bastante sencillo. Pero, ¿y si un desarrollador dirige un proyecto de gran tamaño que no se presta fácilmente a la técnica del fuzzing? En este caso, se debe abordar el análisis dinámico de una manera mucho más cuidadosa.
Decidir los objetivos
Un primer paso fundamental es decidir las metas. Podría emplearse el fuzzing para la detección de problemas de seguridad, o tal vez de cualquier tipo de problemas de exactitud. Uno de los múltiples beneficios del fuzzing es que detecta muchos problemas de baja gravedad, que podrían no hallarse nunca a través de su uso normal. Pueden parecer exactamente como vulnerabilidades de seguridad, con la única diferencia de que no se rebasa límite de confianza alguno.
Por ejemplo, a través del fuzzing podría verse cómo una aplicación que solamente espera una entrada proveniente de la salida de una herramienta de confianza puede provocar incidencias que nunca se encontrarían a través del uso normal. ¿Hay otras maneras de conseguir una entrada corrupta en la aplicación? Si es así, hay una vulnerabilidad de seguridad. Si no, puede tratarse de un problema de corrección de ‘baja prioridad’ que nunca se haya resuelto. Es decir, se debe definir claramente en los objetivos que se abordarán todos los temas, en lugar de únicamente en los relativos a la seguridad. Al establecer las expectativas para el análisis dinámico de antemano, se puede ahorrar mucho tiempo y evitar frustraciones.
Entender los límites de confianza
Documentarse y comprender exactamente dónde debe producirse la comprobación de errores es vital para la eficacia del proceso. A los más experimentados profesionales de la seguridad les resulta fácil crear un modelo mental de gran seguridad de modo que cada función compruebe cada entrada a la defensiva. Sin embargo, en el mundo real, es más complejo que eso. Este tipo de hipervigilancia supone un desperdicio enorme y, por ello, no sobrevive en producción.
Al realizar un trabajo adicional a fin de establecer un modelo mental correcto de los límites de seguridad para el proyecto, el equipo de análisis podrá comprender mejor en qué partes del flujo de control del programa se deben realizar los controles correspondientes y dónde se pueden omitir, garantizando así un uso más eficiente del tiempo disponible.
Segmentar el proyecto en base a su interfaz
Diferentes ‘fuzzers[2]‘ poseen diferentes especialidades. Por lo tanto, se debería segmentar el proyecto en áreas específicas para los diferentes tipos de fuzzers en base a su interfaz: archivos, red, API.
Explorar las herramientas existentes
Continuamente se desarrollan nuevas herramientas de fuzzing, mientras las existentes se actualizan con nuevas funcionalidades. Al mantenerse al día con estos desarrollos, es posible descubrir nuevos incrementos de eficiencia que pueden hacer que un proceso de fuzzing funcione de una forma más adecuada, incluso si solo ayuda dentro de un subconjunto del proyecto.
Desarrollar herramientas de forma interna
Podría producirse una situación cuyo requisito sea llevar a cabo un análisis dinámico en un gran proyecto de lenguaje mixto que no se adapte a las herramientas existentes. En este caso, vale la pena considerar escribir un fuzzer específico para las API del proyecto y generar entradas aleatorias basadas en el mismo, añadiendo una gran cantidad de asertos que estén activos, al menos, durante el fuzzing. Crear un fuzzer es relativamente sencillo si está claro cómo es la API y cómo se puede inspeccionar: se toma un generador de números aleatorios, se configura un contenedor aislado o VM y adelante.
¿Realmente vale la pena el fuzzing?
Una crítica común de las herramientas de fuzzing es que, después de haber funcionado durante un tiempo, dejan de encontrar errores. Pero el fallo obvio aquí es que su objetivo no es exactamente encontrar errores, del mismo modo que no es lógico ejecutar un conjunto de pruebas automatizado porque haya pocas regresiones. El mismo razonamiento se aplica al fuzzing. Si las herramientas de fuzzing ya no encuentran errores, se debe considerar un éxito antes de pasar a detectar aquellas amenazas más difíciles de localizar.
¿Cuánto trabajo?
La parte que consume más tiempo del proceso de fuzzing es encontrar la mejor herramienta para ejecutarlo. Si ya existe una herramienta que funciona con el proyecto o que incluso cubre un subconjunto del mismo, entonces solo es cuestión de ejecutarla. A medida que la herramienta encuentre problemas en el sistema, se irá decidiendo si corregir los fallos de baja prioridad y esto dará forma al desarrollo de los límites de confianza del proyecto. Puede producirse una incidencia para la cual se cree y se envíe un parche sólo para que resulte rechazado debido a que una entrada incorrecta generada por el fuzzer nunca alcanzaría a esa parte del proyecto. Esto podría hacer que añadir una comprobación sea demasiado costoso.
Cualquiera que sea el enfoque que se adopte, asegúrese de que esté bien establecido en procesos documentados para que los futuros desarrolladores pueden consultarlo y avanzar a partir de él en el futuro.
[1] Se llama fuzzing a las diferentes técnicas de testeo de software capaces de generar y enviar datos secuenciales o aleatorios a una o varias áreas o puntos de una aplicación, con el objeto de detectar defectos o vulnerabilidades existentes en el software auditado (Fuzzing y seguridad – José Miguel Esparza Muñoz, 2007).
[2] Fuzzer: herramienta que permite utilizar técnicas de fuzzing.