mapa telescopio, realizando una auditoría de código segunda parte
Auditoría de código,  Desarrollo seguro

Realizando una auditoría de código: Segunda parte

En esta serie de tres artículos describo todas las tareas realizando una auditoría de código o análisis de código estático (SAST). El principal proceso llevado a cabo dentro de la gestión de desarrollo seguro de software. En esta segunda parte nos metemos de lleno a la tarea principal y más representativa, la revisión de código.

Si quieres leer acerca de lo que es una auditoría de código mejor empieza por este articulo en el que me centro en su descripción ¿Que es una auditoría de código?.

pantalla con código js, realizando auditoría de código

Si quieres ver cómo realizar una auditoría de código completa, este es el articulo anterior. Primera parte

En la entrega anterior vimos cómo identificar el componente software objeto de la auditoría y su entorno, la obtención de su código y realizamos un breve análisis sobre este. Después de esto vamos buscar vulnerabilidades tanto con ejecución de herramientas automáticas como de forma manual.

Introducción a esta segunda parte

En esta segunda parte de realizando una auditoría de código nos dedicamos a la propia revisión de código. Empezaremos con tareas que nos darán resultados de forma rápida, como la ejecución de herramientas automáticas y la búsqueda de dependencias vulnerables.

Después continuamos con lectura del código dividiendo el proyecto y priorizando algunas partes críticas del componente. Por último vemos como realizar búsquedas por patrones concretos de forma manual y alguna métrica interesante como la complejidad ciclomática.

Ejecución de herramientas automáticas

Un buen primer paso realizando una auditoría de código es ejecutar herramientas especializadas en búsqueda de vulnerabilidades en el código. Principalmente por que con ellas obtendremos una cantidad aceptable de resultados en poco tiempo. Varía según la herramienta que puedas usar y la tecnología del código que estés analizando. Aunque el descubrimiento de vulnerabilidades de esta revisión suele ser muy productivo.

robot, herramientas automáticas analizadores de código estático

Las herramientas de análisis de código automático son muy importantes en una auditoría de código y usualmente suponen una parte importante del descubrimiento de vulnerabilidades. Sin embargo, a pesar de ser un buen primer paso, estas herramientas no pueden considerarse una cobertura de auditoría completa del código. Con este tipo de herramientas descubriremos vulnerabilidades que tienen registradas en sus bases de datos y son detectables mediante patrones concretos. La revisión manual por parte de un auditor que se describirá más adelante, sigue siendo necesaria.

No entraremos en ninguna herramienta concreta, ya que la elección de una u otra no supone una diferencia en esta descripción de procedimiento. Además suelen ser muy fáciles de usar y la mayor dificultad común de estas, es ajena a ellas y se trata de la interpretación de resultados. Este punto lo describiremos a continuación.

A continuación pongo unos ejemplos de herramientas de análisis estático de código, especializadas en búsqueda de vulnerabilidades. Estas son las más reconocidas:

realizando auditoría de código segunda parte

Interpretación de resultados

Cada resultado obtenido por una herramienta automática debe ser correctamente interpretado para el contexto concreto de la aplicación. Debemos entender bien por un lado la posible vulnerabilidad que apunta la herramienta y por otro lado en qué medida esta vulnerabilidad es tal en el código concreto de la aplicación auditada.

Para interpretar el tipo de vulnerabilidad que registra la herramienta automática, habitualmente la descripción facilitada por la base de datos de esta es bastante clara y completa. Después mediante la revisión manual del código concreto al que hace referencia, analizamos la potencial vulnerabilidad en el contexto concreto que nos ocupa. De esta manera descartamos falsos positivos o confirmamos si la vulnerabilidad está expuesta realmente y en qué medida es explotable en la práctica, valorando por lo tanto su gravedad.

punto de vista, objetivo fotografía

Ejemplo de interpretación de una vulnerabilidad muy grave en un contexto concreto

Seguiremos con un ejemplo para ilustrar una interpretación de un resultado automático. Con una librería de parseo de documentos XML, que a causa de una configuración incorrecta, podría llegar a permitir un ataque de tipo XXE. La herramienta señala la potencial vulnerabilidad como muy grave, nosotros debemos analizar si alguna posibilidad de entrada de datos externa puede llegar hasta ese punto del código y por lo tanto explotarla. Para esto analizamos el posible flujo de ejecución hasta la clase señalada.

Si por ejemplo, la librería solo se usa a partir de un XML almacenado en sistema de archivos interno y la entrada de datos del usuario que se escribe sobre el XML antes de su procesamiento, es validada y controlada correctamente. Ocurriría que la potencial vulnerabilidad existe, sin embargo al controlarse correctamente la entrada, esta no puede llegar a ser explotada con el código actual desde el exterior. La vulnerabilidad existe pero su criticidad se reduce considerablemente debido a que no es explotable actualmente.

En el ejemplo descrito, el resultado devuelto por la herramienta automática puede valernos para describir el detalle técnico de la vulnerabilidad y cómo sería su explotación y potencial impacto. Sin embargo, debemos completar la descripción y valoración del resultado en el informe, indicando el contexto concreto de la aplicación. Describiremos la dificultad de explotación actual, que sería solo posible con acceso al sistema de archivos. Reduciremos su gravedad. Describiremos el riesgo de que una posible nueva versión del código si agregue la posibilidad de subida del archivo XML y por lo tanto su explotación, etc… La redacción del informe será descrita en la siguiente y última entrega del procedimiento de auditoría.

Búsqueda de vulnerabilidades conocidas

Continuamos con otra actividad interesante para hacer al principio realizando una auditoría de código. Esta es buscar vulnerabilidades ya conocidas de forma pública y registradas. Esto se debe a que de igual manera que ejecutar herramientas automáticas de análisis de código estático, este paso puede darnos resultados invirtiendo poco tiempo de búsqueda.

realizando auditoría de código segunda parte, fotografía antigua hombre con megáfono

Para hacerlo podemos buscar en los archivos de configuración las dependencias del código y sus versiones concretas. Con estos datos podemos buscar en sitios como «CVE Details». De esta manera comprobamos si esas librerías que usa el código e incluye en el proyecto tienen vulnerabilidades ya descubiertas, registradas y compartidas de forma pública. Así como en el punto anterior en el que trabajamos con resultados de herramientas automáticas. Debemos interpretar la vulnerabilidad descubierta para el contexto concreto del código que auditamos, ya que el riesgo puede variar mucho.

Según la plataforma y lenguaje de programación del componente software objeto de la auditoría. Encontrarás los archivos de configuración donde podemos ver dependencias y versiones concretas de estas. Por ejemplo en java puede ser el archivo «pom.xml» en la raíz del directorio, si este es plataforma java y usa maven como framework para la construcción del proyecto.

Lectura del código

Has llegado al punto más interesante y probablemente en el que descubrirás las vulnerabilidades de mayor gravedad realizando una auditoría de código. Es la fase de auditoría que más tiempo de inversión requiere, pero la más productiva. La propia revisión de código realizada por nosotros. Esta revisión profundiza mucho más allá de la búsqueda automática por patrones ya realizada.

lupa, realizando auditoría de código segunda parte

A continuación vamos a ver cómo podemos revisar el código del proyecto de forma óptima: La forma más cómoda, como organizarnos para dar la mayor cobertura posible y los lugares prioritarios por donde comenzar.

Montaje de entorno de desarrollo y posibilidad de DAST

Dependiendo del tiempo que tengamos disponible, si este no es apretado, merece la pena intentar montar un entorno de desarrollo funcional de la aplicación. En este entorno de desarrollo podremos realizar pruebas dinámicas o DAST, en combinación con nuestro análisis estático o SAST. Podremos realizar debug de la aplicación, establecer puntos de parada y visualizar con más claridad el flujo de ejecución.

Lo anterior nos dará mucha comodidad y una visión extra sobre el componente software. No es imprescindible para realizar la auditoría de código por lo que será un punto opcional. En este punto lo ideal es disponer de ayuda e instrucciones por parte del equipo de desarrollo. Es algo que resulta muy práctico, no obstante habitualmente no se dispone de tiempo ni recursos suficientes como para disponer de esta facilidad. En este caso podemos confirmar y registrar en el informe que no ha sido posible montar el entorno de desarrollo.

lapiceros escritura, revisión de código

Revisión manual

Independientemente de si podemos montar el entorno de desarrollo o no. Lo que siempre vamos a hacer el leer la mayor cantidad de código posible. En este punto nos interesa disponer de un entorno de revisión/lectura lo más cómodo posible, en este enlace te explico como montarlo con una buena herramienta.

División del componente software objetivo en partes

Según el número de líneas de código a revisar y el tiempo disponible. Va a resultar problemático querer abarcar todo y menos sin una organización clara. Debemos realizar una división del componente software en varias partes. Estas partes nos ayudarán mucho dividiendo su revisión y nos dará visibilidad sobre la cobertura que estamos realizando. Aunque el objetivo es realizar una cobertura completa.

mapa de un piso, partes del código del proyecto

Según el componente software y su arquitectura esta división variara. La principal división será por propia arquitectura. Si por ejemplo el componente es cliente-servidor dividimos capa de presentación, lógica de negocio y acceso a base de datos. Las siguientes subpartes pueden venir también dadas por identificar sub-componentes orgánicos o también atendiendo a identificar funcionalidades independientes que ofrece el componente. Es aconsejable llevar un breve registro de esto, anotando las partes revisadas.

Revisión de puntos calientes o checklist

Además de dividir en partes el componente. Realizando una auditoría de código vamos a localizar ciertos puntos críticos, que por la funcionalidad que ofrecen son especialmente sensibles de exponer vulnerabilidades. Estos deben ser puntos prioritarios de revisión por los que empezar. Estos puntos nos servirán como guía e ideas para descubrir vulnerabilidades.

realizando auditoría de código segunda parte, cuaderno, checklist auditoría

A continuación expongo unas breves listas de comprobación o checklist basadas en lo señalado por OWASP en su «Code Review Guide». Esta guía de revisión de código es el recurso de más calidad que puedes encontrar sobre el tema, OWASP es la organización que lidera a nivel mundial en formación y estandarización de desarrollo seguro y auditorías de código.

Actualmente esta guía de revisión de código se encuentra en versión estable 1.1 aunque se está desarrollando la versión 2.0 y se espera para enero de 2011. En la página 196 la versión 2.0 antes enlazada, encontramos un checklist especialmente extenso e interesante. Este checklist es el que te recomiendo usar. Aunque yo para el articulo voy a hacer un resumen genérico de los puntos más importantes.

Puntos críticos según control de seguridad

Según el tipo de control técnico de seguridad. Primero localizamos el punto del código que implementa el control de seguridad y después revisamos la implementación y validez de este:

  • Validación de datos de entrada y codificación: La aplicación realiza una correcta y completa validación de datos y archivos de entrada en cuanto a forma y tamaño? Los formatos de codificación usados y sus conversiones pueden representar un problema?
  • Gestión de identidad y acceso: Validamos los controles de seguridad implicados atendiendo a los atributos que pretendan garantizar: Autenticación y autorización.
  • Configuración segura: Las opciones seleccionadas en los archivos de configuración de servicios, frameworks y librerías son seguras? Se revisan en el paso a producción?
  • Criptografía: Los mecanismos y esquemas de encriptación usan algoritmos suficientemente robustos? Se implementan de forma correcta? Validamos estos mecanismos y esquemas atendiendo a los atributos que pretendan garantizar: Confidencialidad, integridad, autenticidad y no-repudio.
  • Gestión de sesiones de usuario: La configuración y control de sesión es correcta? La funcionalidad limita y controla aspectos de las sesiones tales como duración, CSRF, localización y número?
  • Exposición de información sensible: Cuánta información puedo llegar a obtener sobre el componente y su sistema sin estar autenticado? y una vez autenticado/autorizado?
  • Manejo de excepciones: La aplicación se comporta correctamente ante problemas y errores? Realiza una buena gestión de las excepciones?
  • Trazabilidad de la aplicación: La aplicación realiza detección y registro de usos maliciosos o anómalos? En caso de incidente de seguridad ofrece información suficiente como para ayudar en una respuesta y análisis forense?
realizando auditoría de código segunda parte, subrayado de palabra ideas, en un papel

Puntos críticos según vulnerabilidades del top ten de OWASP

Según tipo de vulnerabilidad a descubrir. A partir de tener la posible vulnerabilidad en mente, buscamos puntos del código donde esta pudiera existir y explotarse. Empezamos por el top ten de OWASP, las famosas diez vulnerabilidades que la organización remarca por su mayor riesgo:

  • Inyección: Ataques de tipo XSS, SQLi, OSi, LDAPi.
  • Autenticación rota: Compromiso de contraseñas, tokens, sesión o algún otro tipo de fallo de configuración que permite suplantación de identidad.
  • Exposición de datos sensibles: Conseguir información sensible para la que no deberíamos tener acceso.
  • Entidades externas de XML (XXE): Este es un tipo de ataque muy concreto que se consigue realizar mediante lenguaje de marcado XML, usando su funcionalidad extendida de entidades externas.
  • Control de acceso roto: Una vez autenticado en la aplicación o servicio, conseguir realizar acciones para las que no estamos autorizados. Ya sea por que nuestro nivel de permisos no debería permitirlo o por que son de otro usuario.
  • Security misconfigurations: Configuraciones por defecto o erróneas que nos permitan hacer cualquier uso malicioso.
  • Cross site scripting (XSS): En la línea del primer ítem de la lista, pero en este caso haciendo referencia concreta a una inyección de tipo XSS. El caso habitual de este ataque es conseguir ejecutar código javascript en el navegador de la víctima cuando ésta usa una web. Para esto primero debes introducir o inyectar en el sistema o parte de este, el código malicioso.
logo organización owasp

Puntos críticos según tipo de vulnerabilidad

Este último checklist para coger ideas. Es parecido al anterior pero más genérico, abarcando tipos de vulnerabilidad de una forma menos concreta:

  • Buffer Overruns and Overflows
  • OS Injection
  • SQL Injection
  • Data Validation
  • Cross-Site Scripting
  • Cross-Site Request Forgery
  • Logging Issues
  • Session Integrity
  • Race Conditions

Búsqueda de vulnerabilidades comunes según la plataforma

Continuamos con otra buena práctica para descubrir vulnerabilidades realizando una auditoría de código. Esta consiste en buscar en internet los problemas de seguridad más comunes en los tipos de componente software que sean similares al tuyo. Puedes buscar problemas de seguridad comunes a la tecnología que implemente el componente. Por ejemplo una búsqueda de este estilo «most common security issues using .net Web APIs».

problemas comunes de seguridad según tecnología,escena de it crowd

Con este tipo de búsquedas estamos al día de lo que ocurre con más frecuencia y nos sirve para obtener ideas de posibles ataques. En la valoración de riesgos la probabilidad de que una vulnerabilidad se explote es igual de importante al posible impacto negativo que tuviera. Cuando se valora la criticidad de una vulnerabilidad, la complejidad de ataque de esta es importante. Las herramientas de ataque suelen desarrollarse para problemas comunes y conocidos de la tecnología objeto y facilitan en gran medida estos.

Búsqueda por patrones

Para movernos por el código realizando la revisión usaremos búsquedas. A continuación describo ejemplos de que buscar y cómo hacerlo.

telescopios, realizando búsquedas por patrones

Que buscar? Ejemplos de patrones

Los patrones que busques realizando una auditoría de código cambiarán totalmente. Dependiendo de la tecnología usada y otras particularidades del código que audites. Para ver ejemplos prácticos, en la nueva versión 2.0 de la code review guide que está desarrollando OWASP. En el apartado «code crawling» hay una lista de estos. Aunque según el código y su lectura irás viendo cuáles usar.

Además del código y los nombre de variables estrictamente, también nos interesan los comentarios y los TODOs. Por poner un ejemplo variado de patrones podrían ser: «password», «contraseña», «problema», «TODO:», «validate», «<form», «hash», «SHA-1», «import javax.servlet.http.HttpServletRequest», «error de», «cambiar esto», «DNI», etc…

Cómo buscar? Expresiones regulares

Cómo hacerlo? Te recomiendo aprender a usar expresiones regulares de forma ágil para localizar los puntos del código que te interese de forma rápida. En el apartado cheatsheet he puesto ejemplos de estos desde bash. Las expresiones regulares sencillamente están para eso, son el método avanzado para realizar búsquedas y multiplicarán tu capacidad haciéndolo.

división de una expresión regular

Poniendo un ejemplo, vamos a hacer una expresión regular para localizar todos los tags usados en los comentarios. Estos son por ejemplo los tag «TODOs» aunque pueden usarse más. Si queremos localizar solo esto con el menor número de falsos positivos e intentando que sea de forma independiente del lenguaje de programación. Podríamos hacerlo estableciendo los diferentes tags usados en comentarios y ciertos posibles caracteres que pueden llevar delante y detrás:

egrep -Ri "[\/\\ \[\"\'#*.!%=>C\-]{1}(TODO|BUG|FIXME|HACK|UNDONE|XXX)[\ :>,.\-]{1}" --color

En el apartado cheat sheet iré compartiendo más.

Complejidad ciclomática

En este articulo no describo en que consiste esta valiosa métrica del código, ya que tiene un articulo propio «Complejidad ciclomática y calidad del código». La complejidad ciclomática es sin duda el indicador menos conocido y más práctico para medir de forma estimada la calidad de nuestro código. Además en unos valores altos representa tanto un riesgo de seguridad como un problema de calidad. Por lo que realizando una auditoría de código nos señalan lugares de descubrimiento de vulnerabilidades.

Con esta métrica además podemos encontrar verdaderos problemas en el código. Es por esto que un código con una complejidad ciclomática demasiado alta se considera no testeable. Para ilustrar esto nada mejor que el comentario en el código que me encontré un día que decia «aqui esta la madre del cordero» (true story). Se trataba de un método que siempre habia dado problemas en el proyecto. El origen del problema se daba por el propio hecho de que incluso sobre un papel, de forma conceptual, el análisis funcional tenia fallas. Además de la penosa implementación por supuesto.

comic de un monstruo, código con alta complejidad ciclomática

Con herramientas de revisión de código como SonarQube puedes obtener métricas sobre complejidad ciclomática. Sin embargo no te devuelve el calculo a nivel de métodos, si no a nivel de clases completas lo cual es mucho menos interesante. Es mejor alguna sencilla utilidad creada en concreto para el lenguaje de programación/entorno de desarrollo que estés usando.

La herramienta que uses debe devolverte esta métrica a nivel de métodos o funciones y preferiblemente pudiendo ordenar estos los resultados de forma sencilla para el proyecto completo, obteniendo la lista de métodos con el scoring más alto. Ya que es esta lista la que nos interesa para preparar el resultado de auditoría.

Conclusiones

Espero que te haya gustado este extenso segundo articulo de realizando una auditoría de código. Mi idea es ofrecer una visión completa sobre todas las tareas que implica una auditoría de código en profundidad. Y también mantener actualizado y mejorar en lo posible este contenido.

Si tienes cualquier duda, corrección o mejora sobre el procedimiento te agradecería que me contactes. La próxima y última entrega tratara del informe de auditoría. Las claves para realizar el mejor entregable posible de nuestro trabajo.

Referencias y recursos para profundizar en el tema:

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

error

Enjoy this blog? Please spread the word :)

error: