In this article
Descifrar los CVE: guía práctica para evaluar y mitigar riesgos de seguridad
Resumen
Exploremos el mundo de las vulnerabilidades y exposiciones comunes (CVE) con ejemplos paso a paso para evaluar si un CVE afecta tu proyecto y estrategias prácticas para mitigarlo eficazmente. Esta guía te ayudará a enfrentar las vulnerabilidades de seguridad. No dejes pasar las alertas de CVE: aprende a abordarlas con confianza y eficiencia.
Contenido
Como desarrolladores, estamos constantemente haciendo malabares con varios proyectos y priorizando correcciones de errores y mejoras de funciones. Sin embargo, en medio de este ritmo frenético, a menudo nos topamos con la inquietante realidad de que nuestros proyectos pueden ser vulnerables a vulnerabilidades y exposiciones comunes (CVE). Cada día aumenta el número de CVE reportados, lo que plantea un desafío considerable para mantener la seguridad de nuestros proyectos. Pero no te preocupes: quiero compartir estrategias prácticas para abordar este problema de frente. En este análisis, describiré varios enfoques para integrar sin complicaciones la revisión de CVE en tu flujo de trabajo de desarrollo, y así ayudarte a proteger tus proyectos con confianza.
Si no tenemos una estrategia clara para mitigar los CVE, es muy fácil sentirnos abrumados por la cantidad de CVE que se descubren cada día, sobre todo en el ecosistema Node.js, donde es muy común que nuestras aplicaciones tengan muchas dependencias. Ulises Gascón. Node.js para principiantes (capítulo 15. Protección de aplicaciones web)
¿Cómo revisar un CVE?
Tomemos como ejemplo CVE-2020-8203. Esta vulnerabilidad de contaminación de prototipos afecta a la popular biblioteca Lodash.
Según las herramientas que usemos para recibir avisos sobre las vulnerabilidades de nuestro proyecto, podríamos encontrar distintos informes que repiten más o menos la misma información:
Como puedes ver, ambas imágenes comparten información similar (principalmente metadatos) sobre la vulnerabilidad. Sin embargo, cada descripción aporta información diferente que puede enriquecer nuestro análisis.

Revisa la gravedad
Primero, podemos usar la gravedad para priorizar el trabajo. Se basa en una escala del uno al diez y utiliza el Sistema de puntuación de vulnerabilidades comunes (CVSS) para determinar el valor. Esta puntuación también se desglosa y puede aportarnos mucho contexto. La idea es priorizar la aplicación de parches según la criticidad cuando tengamos varias vulnerabilidades que revisar.
Ten en cuenta que los resultados pueden variar según el proveedor de información. En la imagen de abajo vemos que Snyk asigna 8.2, mientras que Red Hat y NVD asignan 7.4.

No importa qué sistema de puntuación sigas, siempre y cuando uses el mismo en todo momento. En general, los CVE críticos o altos son importantes de revisar, mientras que los CVE de menor gravedad pueden darnos más tiempo.
Para que quede muy claro: una vez que un CVE se hace público, todo el mundo se entera, por lo que los actores maliciosos pueden integrar fácilmente estos CVE críticos en su arsenal y empezar a usarlos para explotar sistemas si encuentran la manera.
Si no sabes qué tan rápido pueden actuar los actores maliciosos, consulta este informe de honeypot.
Obtén más información
El siguiente paso es obtener toda la información posible sobre esta vulnerabilidad y su posible explotación. A veces, puedes encontrar mucha información revisando los enlaces incluidos en la sección de referencias de la página oficial del informe del CVE.

En este caso, incluso podemos leer el informe original en HackerOne y la conversación entre quien lo reportó y los responsables del mantenimiento. Esto puede aportarnos mucha información. En este caso, podemos encontrar información útil en los informes de Snyk y GitHub.
Aclara la superficie de ataque
A partir del paso anterior, deberíamos tener claro qué versiones de la biblioteca están afectadas (>=4.1.0 <4.17.20) y qué métodos están incluidos en el alcance de esta vulnerabilidad (pick, set, setWith, update, updateWith y zipObjectDeep), teniendo en cuenta que sabemos que se trata de una vulnerabilidad de contaminación de prototipos.
Podemos hacer una comprobación rápida en el repositorio y evaluar si estamos expuestos. Si no usamos estos métodos con estas versiones específicas, podemos confirmar que nuestro código no está afectado y que, en nuestro caso, se trata de un falso positivo.
Esta evaluación puede ser más compleja de lo esperado. En el ecosistema JavaScript, es muy común tener devDependencies en herramientas de desarrollo que no tienen un impacto real en el entorno de producción, por ejemplo, linters, marcos de prueba y ciertas utilidades. Esto depende mucho de la naturaleza específica del proyecto. Podemos usar PM2 localmente para simular un entorno de producción, pero utilizar contenedores y Kubernetes para implementar nuestros proyectos en producción. Esto limita en gran medida los vectores de ataque de los CVE relacionados con PM2, ya que en la mayoría de los casos lo ejecutamos en un «entorno seguro» que controlamos por completo. Sin embargo, ten en cuenta que, aunque el paquete no se use en producción, puede tener efectos negativos en nuestro entorno de desarrollo si se ve comprometido. Por ejemplo, eslint fue comprometido en 2018.
Si no tenemos un argumento claro para descartar esta vulnerabilidad en nuestro proyecto, lo más prudente es considerarnos afectados por ella y posiblemente tener que aplicar una mitigación válida.
Además de la información incluida en la descripción, una buena forma de entender mejor la técnica de ataque es consultar las enumeraciones de debilidades comunes (CWE) mencionadas en el CVE.

En este caso, la CWE mencionada fue CWE-1321: Modificación incorrectamente controlada de los atributos del prototipo de un objeto («contaminación de prototipos»). Esto nos ayuda mucho a encontrar CVE similares y a entender mejor cómo se puede explotar la vulnerabilidad.
En pocas palabras, la CWE es una lista de posibles debilidades y la CVE es una lista de vulnerabilidades que se han descubierto en el mundo real. Ulises Gascón. Node.js para principiantes
¿Hay una prueba de concepto (POC) que pueda explotarse?
No todos los CVE incluyen una prueba de concepto (POC) de la vulnerabilidad reportada que se pueda explotar fácilmente, pero en este caso tenemos una muy clara:
const _ = require('lodash');
_.zipObjectDeep(['__proto__.z'],[123]);
console.log(z); // 123
Además de ayudarnos a entender mejor el problema, esto puede servirnos para probar nuestro código y confirmar que la vulnerabilidad está presente. Supongamos que nuestra aplicación tiene el siguiente código:
const _ = require('lodash');
const normalizeData = (users, ages) => {
return _.zipObjectDeep(users,ages);
}
module.exports = { normalizeData }
Podemos suponer fácilmente que es vulnerable a un ataque de contaminación de prototipos, pero también podemos crear una prueba para asegurarnos de tener los mecanismos necesarios para evitar que estas vulnerabilidades vuelvan a ocurrir. A veces, las bibliotecas introducen regresiones que contienen vulnerabilidades. Podemos prevenirlas con una prueba sencilla, como la siguiente:
const { normalizeData } = require('../utils');
describe('normalizeData behaviour', () => {
it('should return the data structured properly', () => {
const users = ['John', 'Doe'];
const ages = [30, 40];
const result = normalizeData(users, ages);
expect(result).toEqual({ John: 30, Doe: 40 });
});
it('should not be affected by a prototype pollution (CVE-2020-8203)', () => {
normalizeData(['__proto__.z'], [123]);
expect(global.z).toBe(undefined);
});
});
Obviamente, esta prueba fallará porque actualmente somos vulnerables a esta contaminación de prototipos y no hemos hecho ningún cambio en nuestro proyecto para mitigarla.

Figura: uso de Node.js@21 y lodash@4.17.0
Ahora veamos cómo podemos mitigar esta vulnerabilidad.
¿Qué estrategias hay para mitigar un CVE?
Hay varias estrategias para mitigar un CVE. Exploraremos algunas de ellas, ordenadas según el esfuerzo que requieren.
Actualizar o revertir versiones
La forma más recomendada de mitigar un CVE es actualizar la versión de la biblioteca afectada. En este caso, podemos actualizar a lodash@4.17.20 o una versión posterior. Suele ser el mejor método porque quienes mantienen la biblioteca ofrecen una solución para la vulnerabilidad que también probaron con quien la reportó, por lo que podemos tener la certeza de que el parche funciona.
Por lo tanto, si aplicamos este parche (npm i lodash@4.17.20), podemos volver a ejecutar la prueba que creamos antes y asegurarnos de que la vulnerabilidad esté mitigada.

Figura: uso de Node.js@21 y lodash@4.17.21
Aunque parezca una tarea sencilla, puede volverse muy compleja. Quizás tengamos que actualizar o revertir otras dependencias que no sean compatibles con la nueva versión de la biblioteca, o tal vez la versión más reciente incluya otros cambios incompatibles con nuestro código.
En otros casos, el CVE se hizo público antes de que se publicara el parche, por lo que necesitamos explorar otras estrategias para mitigar la vulnerabilidad mientras esperamos el parche definitivo.
Migrar
Si la biblioteca ya no recibe mantenimiento o quienes la mantienen no ofrecen un parche para la vulnerabilidad, quizás tengamos que migrar a otra biblioteca que ofrezca la misma funcionalidad o una similar. Aunque requiere un gran esfuerzo, puede ser una buena inversión si la nueva biblioteca es más segura y recibe más mantenimiento que la anterior.
Así podemos tener la certeza de que, en el futuro, tendremos menos CVE que revisar y podremos administrar las actualizaciones con más facilidad.
Aplicar parches manualmente (hazlo tú mismo)
Si no podemos actualizar la biblioteca ni migrar a otra, quizás tengamos que aplicar el parche nosotros mismos. Esta también es una estrategia válida cuando el CVE se hace público antes de que se publique el parche y necesitamos mitigar la vulnerabilidad cuanto antes mientras esperamos el parche oficial.
Cualquier parche manual requiere un buen conocimiento de la biblioteca y la vulnerabilidad. Aquí podemos basarnos en la POC proporcionada en el informe del CVE para crear nuestro propio parche y usar la prueba que desarrollamos para comprobar que funciona. Veamos cómo crear un parche para esta vulnerabilidad:
const _ = require('lodash');
const normalizeData = (users, ages) => {
const safeUsers = users.filter(user => !/__proto__|prototype|constructor/.test(user));
return _.zipObjectDeep(safeUsers, ages);
}
module.exports = { normalizeData }
Este es un parche sencillo que muestra cómo mitigar la vulnerabilidad, pero para cumplir mejor con los requisitos, consulta el parche oficial, ya que nuestro caso de prueba era muy básico. Ahora podemos volver a ejecutar la prueba y asegurarnos de que la vulnerabilidad esté mitigada.

Figura: uso de Node.js@21 y lodash@4.17.0 con el parche
Estrategias para documentar las decisiones sobre los CVE
Cuando trabajamos en equipo, es importante documentar las decisiones que tomamos sobre los CVE. Esto nos ayudará a llevar un registro de las vulnerabilidades que mitigamos y de las que aún tenemos pendientes. También nos permitirá priorizar el trabajo y asegurarnos de no pasar por alto ninguna vulnerabilidad.
Podemos usar una tabla sencilla para documentar los CVE que estamos revisando, su gravedad, estado, estrategia de mitigación y la fecha en que mitigamos la vulnerabilidad. Así podremos llevar un registro de las vulnerabilidades que mitigamos y de las que aún tenemos pendientes.
CVE | Gravedad | Estado | Estrategia de mitigación | Fecha |
CVE-2020-8203 | 8.2 | Mitigado | Actualizar a lodash@4.17.21 | 2024-04-15 |
Esta tabla se puede actualizar cada vez que revisemos un CVE y compartir con el equipo para que todos estén al tanto de las vulnerabilidades que enfrentamos y las que mitigamos.
Mejor aún, puedes incluir esta tabla en la documentación del proyecto para que forme parte del proceso de distribución del código.
Si usas una herramienta para revisar los CVE, puede ser difícil aclarar el estado de los que se identificaron como falsos positivos o de aquellos para los que aplicamos un parche manual. Por ejemplo, si usas Snyk, puedes marcar el CVE como ignorado.
En nuestro caso, si aplicaste el parche manual, puedes marcar el CVE como ignorado en el informe de Snyk de la siguiente manera: snyk ignore --id=SNYK-JS-LODASH-567746 --reason="manual patch in place with tests".
Esto generará el siguiente contenido en el archivo .snyk:
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.25.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
SNYK-JS-LODASH-567746:
- '*':
reason: manual patch in place with tests
expires: 2024-05-15T17:27:20.339Z
created: 2024-04-15T17:27:20.340Z
patch: {}
En general, el proceso requiere algo de trabajo manual. Se recomienda trabajar en equipo para evitar sesgos y errores humanos en la medida de lo posible.
Lista de verificación
En resumen, estos son los pasos que debemos seguir para revisar un CVE:
Revisar la gravedad del CVE
Obtener más información sobre el vector de ataque
Aclarar la superficie de ataque
Comprueba si hay una POC que se pueda explotar
Evalúa el impacto en tu proyecto
Mitiga la vulnerabilidad si es necesario
Documenta las decisiones tomadas sobre las CVE
Comparte las conclusiones con tu equipo
Recursos adicionales
Si quieres explorar más temas relacionados con la seguridad en Node.js, puedes consultar mi libro Node.js para principiantes, donde abordo muchos temas de seguridad en Node.js.
Protege tu código con información de vanguardia
Conoce todas las funcionalidades de SAST de Snyk Code en solo 30 minutos.