En algunos artículos recientes compartí cómo tú, como desarrollador, puedes ampliar tu conjunto de habilidades con seguridad mediante el uso de las capacidades de New Relic. También profundicé en las formas de mitigar los riesgos de seguridad ocultos de las bibliotecas de software de código abierto. Ambos blogs se centraron en el código de terceros y en cómo puede afectar a la seguridad de sus aplicaciones de software. En este artículo me centraré en la seguridad de tu propio código personalizado; es decir, el código que escribes tú mismo.

En general, hay varios métodos que puede emplear para garantizar la seguridad de su código de origen. Un enfoque habitual consiste en utilizar herramientas de análisis estático del código, que analizan el código fuente antes de implantarlo en distintos entornos, como pruebas, control de calidad y producción. Esta práctica resulta valiosa, ya que evita la inclusión de secretos codificados en el repositorio o en las compilaciones de software. Sin embargo, estas técnicas sólo ofrecen una visión de los aspectos estáticos de su aplicación.

Para hacer frente a esta limitación, el sector suele recurrir a herramientas dinámicas de seguridad de aplicaciones, que realizan pruebas de caja negra examinando la aplicación desde una perspectiva externa. Aunque este enfoque ofrece una perspectiva adicional, sólo revela una faceta de la situación general de la seguridad. La razón esencial de la cuestión es que ninguna de estas soluciones ofrece una visión global.

Las pruebas interactivas de seguridad de aplicaciones (IAST) de New Relic están diseñadas precisamente para resolver este problema. Con IAST, puedes mejorar tu capacidad de observación porque las herramientas de New Relic ya proporcionan una visibilidad excepcional de los aspectos dinámicos de su aplicación desde dentro. La naturaleza de la supervisión del rendimiento de las aplicaciones (APM) es que observamos el código mientras se ejecuta en los sistemas. Esto significa que no sólo vemos el rendimiento y los errores, sino también qué versión del código se ha desplegado en cada instancia del servicio y qué métodos se están activando realmente en función de la actividad en tiempo real de la propia aplicación.

En la observación de sus servicios, los problemas de seguridad de las aplicaciones, procedentes tanto de fuentes propias como de terceros, son cada vez más cruciales. New Relic IAST desempeña un papel clave en este contexto, ya que elimina eficazmente la ambigüedad detectando rápidamente las vulnerabilidades de sus aplicaciones. Proporciona pruebas concluyentes de los exploits y sus ubicaciones precisas dentro de su código. Además, la herramienta ofrece procedimientos de prueba y medidas de mitigación procesables y repetibles, lo que le permite abordar eficazmente los problemas de seguridad en entornos de preproducción o desarrollo antes de que puedan explotarse. Permítanme darles un ejemplo de lo que queremos decir con acción ejecutable y repetible: para cada prueba de seguridad, hay un comando curl asociado (u otra forma de script de prueba) para que puedan volver y realizar la prueba nuevamente a medida que corrigen su código. La misma secuencia de comandos de prueba se puede utilizar para verificar que realmente se ha solucionado sin tener que volver a ejecutar todo el conjunto de pruebas.

Como primer paso hacia la seguridad demostrable, New Relic IAST permite un verdadero "desplazamiento a la izquierda" en la seguridad del software al permitir que los equipos de DevOps y de seguridad trabajen juntos para acelerar la integración continua y los conductos de despliegue continuo (CI/CD) al permitir que las organizaciones se centren en la innovación mientras protegen sus aplicaciones al principio del ciclo de vida de desarrollo.

Otro beneficio clave de centrarse en las vulnerabilidades explotables es que las herramientas tradicionales de seguridad de aplicaciones a menudo carecen de contexto, lo que conduce a un número abrumador de alertas que pueden confundir a los equipos de DevOps y de seguridad, lo que dificulta la identificación de los riesgos de seguridad inmediatos y la verificación de las mejoras de seguridad. Básicamente, New Relic IAST elimina el ruido (falsos positivos) que se encuentra en muchas otras soluciones y proporciona una prueba de explotación para la validación.

He dicho muchas cosas, así que permítanme mostrarles cómo funciona New Relic IAST en la práctica.

Nota: Antes de seguir adelante y compartir cómo empezar con New Relic IAST, quiero señalar un aspecto importante. New Relic IAST simula una batería de ataques reales dentro de su aplicación. Esto significa que se utilizarán datos de prueba para simular y probar vulnerabilidades explotables. Por este motivo, New Relic IAST sólo debe utilizarse en entornos de preproducción.

Agentes de lenguajes de New Relic

Los agentes APM más recientes de New Relic ahora incluyen la capacidad IAST. Todos sabemos lo fácil que es añadir agentes APM a una aplicación para identificar errores y problemas de rendimiento.Eche un vistazo a este ejemplo intuitivo para un entorno .NET. Ahora, sin ningún esfuerzo adicional, también podemos probar tu código personalizado para las vulnerabilidades explotables OWASP Top 10, como los ataques de inyección.  En mi opinión, esta es una ventaja increíble de New Relic IAST y a muchas partes interesadas les encantará:

  • TI, porque no tienen que desplegar otro agente más.
  • Equipos de seguridad, porque esto convierte a los equipos de desarrollo en un multiplicador de la fuerza de seguridad.
  • Tú, como desarrollador, porque ya forma parte de tu cadena de herramientas que ya utilizas.

Esta capacidad es muy fácil de implementar. En mi ejemplo, estoy utilizando la aplicación OWASP Juice Shop de nuevo como un entorno de demostración.

Juice Shop está construido con Node.js, así que sigo adelante y añado el último agente New Relic Node.js (IAST se añadió en la versión v10.3.0) a mi aplicación.

Seguimos el conocido enfoque de ejecutar el siguiente comando dentro de nuestro repositorio

npm install newrelic

Esto instala la librería del agente de New Relic en nuestra aplicación. La forma más sencilla de configurar el agente es definiendo estas variables de entorno:

NEW_RELIC_APP_NAME=juice-shop-mysql
NEW_RELIC_LICENSE_KEY=<your New Relic license key>

Ahora puedes instalar el resto de dependencias de Juice Shop con npm install y ejecutar la aplicación con npm start. Al poco tiempo, los datos aparecerán en tu cuenta de New Relic en la conocida experiencia curada para APM.

Sin ninguna configuración adicional, el agente capturará todas las bibliotecas utilizadas en su aplicación y comprobará si existen vulnerabilidades conocidas. Estos datos se publican en la sección Gestión de vulnerabilidades de tu servicio en New Relic.

Activar New Relic IAST

Ahora que ya tenemos el agente funcionando y reportando vulnerabilidades para librerías de terceros, continuemos habilitando las capacidades de IAST.

Detengo la aplicación y procederé a agregar dos variables de entorno adicionales:

NEW_RELIC_SECURITY_ENABLED=true
NEW_RELIC_SECURITY_AGENT_ENABLED=true

La primera variable de entorno determina si los datos de seguridad se envían a New Relic o no. Cuando está desactivado y NEW_RELIC_SECURITY_AGENT_ENABLED es verdadero, el módulo de seguridad se ejecutará pero no se enviarán datos. La segunda le permite activar o desactivar todas las funciones de seguridad en general.

Una vez definidos éstos, no nos queda nada más por configurar. El agente se encargará del resto por nosotros una vez que la aplicación se ejecute de nuevo.

Simulación de ataques reales

Ahora que la aplicación se está ejecutando de nuevo (ejecutando npm start otra vez), podemos ejecutar nuestras pruebas unitarias habituales. Dentro del repositorio de Juice Shop, todas las pruebas unitarias se encuentran en la carpeta de pruebas. Por ejemplo, puedes ejecutar las pruebas de la API ejecutando npm run test:api. Con sólo activar estas pruebas, el agente identificará el contexto de la solicitud; es decir, una transacción en el contexto de New Relic, incluidos todos los detalles de parámetros, carga útil, etc.

Por ejemplo, nuestra Juice Shop incluye una API para el inicio de sesión de los usuarios de la aplicación. Este punto final recibe cierta carga útil, incluidas las credenciales de usuario para la autenticación del usuario.

Utilicemos la aplicación e intentemos acceder a ella con un usuario existente.

Al cabo de unos instantes, verás que nuestro agente se pone en contacto con tu solicitud. Si el agente identifica un problema, añadirá una vulnerabilidad a la sección de gestión de vulnerabilidades de New Relic. La gran diferencia es que sólo lo hará si la vulnerabilidad es realmente explotable.

Veamos un ejemplo.

Puedes identificar las vulnerabilidades explotables con un signo de exclamación en la columna Explotable. Esto significa que no sólo hemos encontrado un problema, sino que también hemos identificado una prueba de explotación para los mismos. Otro aspecto clave a la hora de analizar las vulnerabilidades es que esta lista de vulnerabilidades se prioriza en función de la gravedad.

Haz clic en el problema de inyección SQL para obtener más información sobre el problema y la capacidad única de New Relic para proporcionar pruebas de explotabilidad. Aquí, los usuarios encontrarán una descripción detallada de la vulnerabilidad y casos específicos de este problema dentro de su código de origen.

Al hacer clic en una de las instancias de Inyección SQL, obtendrás más información sobre la ubicación dentro de tu código fuente y cómo solucionarlo.

Solucionar una vulnerabilidad explotable

Una de las características clave de New Relic IAST es la prueba de repetición que proporciona New Relic. En la sección Test de la pantalla encontrarás un script de prueba que puedes utilizar para probar la explotabilidad. En nuestro caso, ejecutaremos este script para probar que la inyección SQL es un problema real y explotable en Juice Shop.

Abramos la terminal y ejecutemos el siguiente comando que copiamos de la interfaz de usuario de New Relic. Nota: he añadido la URL completa en la primera línea del script. También he ajustado ligeramente el parámetro data-raw.

curl --location --request POST 'https://wxmqzkpgeu.us-east-1.awsapprunner.com/rest/user/login' \
--header 'x-forwarded-proto:https'
--header 'user-agent:Mozilla/5.0 (Macintosh;Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, como Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67;' \
--header 'accept:application/json, text/plain, */*' \
--header 'sec-ch-ua-platform:"macOS"' \
--header 'sec-ch-ua-mobile:?0' \
--header 'sec-fetch-site:same-origin' \
--header 'accept-encoding:gzip, deflate, br' \
--header 'accept-language:de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6;' \
--header 'host:wxmqzkpgeu.us-east-1.awsapprunner.com' \
--header 'cookie:language=es;welcomebanner_status=desestimar;cookieconsent_status=desestimar;' \
--header 'origin:https://wxmqzkpgeu.us-east-1.awsapprunner.com' \
--header 'x-envoy-expected-rq-timeout-ms:120000' \
--header 'content-type:application/json' \
--header 'sec-fetch-dest:empty' \
--header 'x-request-id:dfc1b83b-5e24-4cb6-877b-20913a9cd54f' \
--header 'sec-ch-ua:"Not.A/Brand";v="8", "Chromium";v="114", "Microsoft Edge";v="114";' \
--header 'sec-fetch-mode:cors' \
--header 'x-forwarded-for:79.252.203.104' \
--header 'referer:https://wxmqzkpgeu.us-east-1.awsapprunner.com/' \
--header 'x-envoy-dirección-externa:79.252.203.104' \
--data-raw '{"email": "1'"'"' OR TRUE; #","contraseña":"12345"}'

Después de ejecutar este script, observarán que obtenemos algunos datos JSON con información del usuario admin de Juice Shop. Por supuesto, esto no es lo que la aplicación debería proporcionar y demuestra que en realidad tenemos un problema crítico y un riesgo asociado.

La ubicación de la vulnerabilidad proporcionada por New Relic IAST nos muestra que uno de los problemas de inyección SQL se encuentra en `/rest/user/login`.

Echemos un vistazo al código fuente de la aplicación y abramos el archivo `server.ts`. (en la raíz del repositorio). En algún lugar alrededor de la línea 543, verás qué método está siendo llamado realmente cuando alguien accede al endpoint `/rest/user/login`. Se ejecuta el método `login()` que se encuentra en `/routes/login.ts`. Como desarrollador, puedo ver cómo se concatena la consulta SQL añadiendo la entrada del usuario directamente en la sentencia SQL (línea 36).

models.sequelize.query(`SELECT * FROM Users WHERE email = '${req.body.email || ''}' AND password = '${security.hash(req.body.password || '')}' AND deletedAt IS NULL`, { model: UserModel, plain: true })

Esto no es en absoluto una buena práctica y los desarrolladores deberían evitar crear consultas SQL de este tipo.

Lo ideal sería utilizar consultas parametrizadas. En el framework de `sequelize` se habla de sustituciones. Así pues, lo ideal sería utilizar parámetros en la propia consulta y sustituirlos por el contenido utilizando el concepto de sustituciones. El código actualizado debería ser algo parecido a esto:

models.sequelize.query(`SELECT * FROM Users WHERE email = :email AND password = :password AND deletedAt IS NULL`,
      {
        model: UserModel,
        plain: true,
        replacements: {
          email: req.body.email || '',
          password: security.hash(req.body.password || '')
        }
      })

Sigamos adelante, arreglemos este problema de código y volvamos a desplegar la aplicación. Ahora que la nueva versión de nuestra aplicación está en funcionamiento y los datos se envían a New Relic, podemos volver a ejecutar nuestro script curl de prueba.

Una vez que se ha terminado de ejecutar, no obtenemos el mismo resultado de antes; es decir, las credenciales del usuario administrador de Juice Shop. Pero, recibimos un mensaje que indica que hemos proporcionado un correo electrónico o una contraseña no válidos. Esto es lo que esperábamos y demuestra que nuestra corrección en el código fuente de origen ha solucionado el error.

Si ahora volvemos a nuestra interfaz de usuario de New Relic y navegamos hasta la sección Gestión de vulnerabilidades de nuestra aplicación, observarás que seguimos identificando las dos vulnerabilidades explotables. Sin embargo, también observamos que el problema de inyección SQL se detectó por última vez hace unas cuatro horas, pero el problema de XSS reflejado sigue existiendo, ya que se volvió a notificar después de nuestra implementación.

Conclusión

Lo que te he mostrado en este ejemplo es un ciclo completo de ejecución de un análisis de seguridad de aplicaciones interactivo mediante el examen de tu aplicación y la ejecución de pruebas para encontrar vulnerabilidades explotables.

No sólo identificamos un problema, sino que también recibimos ciertos casos con secuencias de comandos de prueba que podíamos reproducir para demostrar realmente que los problemas identificados son explotables. Después de solucionar el problema en nuestro código de origen, creamos una nueva versión de nuestra aplicación y la desplegamos. Esta vez las pruebas de nuestro agente New Relic IAST no pudieron ejecutarse con éxito para esta área específica dentro de nuestra aplicación. He podido comprobar el éxito de la corrección ejecutando el mismo script de prueba y validar que ya no se trata de un problema en nuestra aplicación.