Métricas de tiempo hasta parches: un enfoque de análisis de supervivencia con Qualys y Elastic
Introducción
Comprender la rapidez con la que se corrigen las vulnerabilidades en diferentes entornos y equipos es fundamental para mantener una postura de seguridad estable. En este artículo, describimos cómo aplicamos el análisis de supervivencia a los datos de gestión de vulnerabilidades (VM) de Qualys VMDR, empleando el Elastic Stack. Esto nos permitió no solo confirmar suposiciones generales sobre la velocidad del equipo (qué tan rápido los equipos completan el trabajo) y la capacidad de remediación (cuánta reparación pueden asumir), sino también obtener información medible. Dado que la mayoría de nuestros datos de seguridad están en el Elastic Stack, este proceso debería ser fácilmente reproducible a otras fuentes de datos de seguridad.
Por qué lo hicimos
Nuestra motivación principal fue pasar de suposiciones generales a conocimientos respaldados por datos sobre:
- La rapidez con la que los diferentes equipos y entornos parchean las vulnerabilidades
- Si el rendimiento de la aplicación de parches cumple con los objetivos de nivel de servicio (SLO) internos
- Dónde suelen producir cuellos de botella o retrasos
- ¿Qué otros factores pueden afectar el rendimiento de la aplicación de parches?
¿Por qué el análisis de supervivencia? Una mejor alternativa al tiempo medio para remediar
El tiempo medio de corrección (MTTR) se usa comúnmente para rastrear la rapidez con la que se parchean las vulnerabilidades, pero tanto la media como la mediana sufren limitaciones significativas (proporcionamos un ejemplo más adelante en este artículo). La media es muy sensible a los valores atípicos[^1] y asume que los tiempos de remediación están equilibrados uniformemente alrededor del tiempo promedio de remediación, lo que rara vez es el caso en la práctica. La mediana es menos sensible a los extremos, pero descarta información sobre la forma de la distribución y no dice nada sobre la larga cola de vulnerabilidades lentas para parchear. Ninguno de los dos tiene en cuenta los casos no resueltos, es decir, las vulnerabilidades que permanecen abiertas más allá de la ventana de observación, que a menudo se excluyen por completo. En la práctica, las vulnerabilidades que permanecen abiertas por más tiempo son precisamente las que más deberían preocuparnos.
El análisis de supervivencia aborda estas limitaciones. Originado en contextos médicos y actuariales, modela datos de tiempo hasta el evento al tiempo que incorpora explícitamente observaciones censuradas, lo que significa en nuestro contexto vulnerabilidades que permanecen abiertas. (Para obtener más detalles sobre su aplicación a la gestión de vulnerabilidades, recomendamos encarecidamente "The Metrics Manifesto"). En lugar de colapsar el comportamiento de remediación en un solo número, el análisis de supervivencia estima la probabilidad de que una vulnerabilidad permanezca sin parches a lo largo del tiempo (p. ej. El 90% de las vulnerabilidades se corrigen en 30 días). Esto permite evaluaciones más significativas, como la proporción de vulnerabilidades parcheadas dentro de SLO (por ejemplo, dentro de 30, 90 o 180 días).
El análisis de supervivencia nos proporciona una función de supervivencia que estima la probabilidad de que una vulnerabilidad permanezca sin parches a lo largo del tiempo.
::: Este método ofrece una mejor visión del rendimiento de la corrección, lo que nos permite evaluar no solo cuánto tiempo persisten las vulnerabilidades, sino también cómo difiere el comportamiento de la corrección entre sistemas, equipos o niveles de gravedad. Es particularmente adecuado para los datos de seguridad, que a menudo están incompletos, sesgados y son resistentes a las suposiciones de normalidad. :::
Contexto
Aunque aplicamos el análisis de supervivencia en diferentes entornos, equipos y organizaciones, en este blog nos centramos en los resultados para el entorno de producción de Elastic Cloud.
Cálculo de la edad de vulnerabilidad
Existen diferentes métodos para calcular la edad de la vulnerabilidad.
Para nuestras métricas internas, como el SLO de cumplimiento de vulnerabilidades, definimos la edad de vulnerabilidad como la diferencia entre la última vez que se encontró una vulnerabilidad y la primera vez que se detectó (generalmente unos días luego de la publicación). Este enfoque tiene como objetivo penalizar las vulnerabilidades que se reintroducen desde una imagen base obsoleta. En el pasado, nuestras imágenes base no se actualizaban con la frecuencia suficiente para nuestra satisfacción. Si se crea una nueva instancia, las vulnerabilidades pueden tener una antigüedad significativa (por ejemplo, 100 días) desde el primer día de descubrimiento.
Para este análisis, nos parece más relevante calcular la edad en función del número de días entre la última fecha encontrada y la primera fecha encontrada. En este caso, la edad representa el número de días que el sistema estuvo efectivamente expuesto.
Estrategia de "parchear todo"
En nuestro entorno de nube, mantenemos una política para parchear todo. Esto se debe a que usamos casi exclusivamente la misma imagen base en todas las instancias. Dado que Elastic Cloud funciona completamente en contenedores, no hay paquetes de aplicaciones específicos (por ejemplo, Elasticsearch) instalados directamente en nuestros sistemas. Como resultado, nuestra flota sigue siendo homogénea.
Canalización de datos
Ingerir y mapear datos en el Elastic Stack puede ser engorroso. Afortunadamente, tenemos muchas integraciones de seguridad que las manejan de forma nativa, siendo Qualys VMDR una de ellas.
Esta integración tiene 3 intereses principales sobre los métodos de ingesta personalizados (p. ej. guiones, ritmos, ...):
- Enriquece de forma nativa los datos de vulnerabilidad de la base de conocimientos de Qualys, que agregan identificadores de CVE, información de inteligencia de amenazas, ... sin necesidad de configurar Enriquecer canalizaciones.
- Los datos de Qualys ya están asignados al Elastic Common Schema, que es una forma estandarizada de representar datos, ya sea que provengan de una fuente u otra: por ejemplo, los CVE siempre se almacenan en vulnerability.id de campo, independiente de la fuente.
- Ya se configuró una transformación con la vulnerabilidad más reciente. Este índice se puede consultar para obtener el estado más reciente de las vulnerabilidades.
Configuración de la integración del agente de Qualys
Para el análisis de supervivencia, necesitamos ingerir vulnerabilidades activas y parcheadas. Para analizar un periodo específico, debemos establecer el número de días en el campo max_days_since_detection_updated. En nuestro entorno, ingerimos datos de Qualys a diario, por lo que no es necesario ingerir un largo historial de datos fijos, ya que ya lo hicimos.
La integración del agente elástico VMDR de Qualys se configuró con lo siguiente:
| Propiedad | Value | Comentario |
|---|---|---|
| (Sección de configuración) Nombre de usuario | ||
| (Sección de configuración) Contraseña | Dado que no hay claves API disponibles en Qualys, solo podemos autenticarnos con la autenticación básica. Cerciorar de que el inicio de sesión único (SSO) esté desactivado en esta cuenta | |
| URL | https://qualysapi.qg2.apps.qualys.com (para US2) | https://www.qualys.com/platform-identification/ |
| Intervalo | 4h | Ajústelo en función del número de eventos ingeridos. |
| Parámetros de entrada | show_asset_id=1&include_vuln_type=confirmado&show_results=1&max_days_since_detection_updated=3&estado=Nuevo,Activo,Reabierto,Fijo&filter_superseded_qids=1&use_tags=1&tag_set_by=nombre&tag_include_selector=Todo&tag_exclude_selector=Cualquiera&tag_set_include=Estado:En ejecución&tag_set_exclude=Estado: terminado,estado:detenido,estado:obsoleto&show_tags=1&show_cloud_tags=1 | show_asset_id=1: recuperar el ID del activo show_results=1: detalles sobre cuál es el paquete instalado actualmente y qué versión debe instalar max_days_since_detection_updated=3: filtrar cualquier vulnerabilidad que no se actualizó en los últimos 3 días (p. ej. parcheado con más de 3 días) status=Nuevo,Activo,Reabierto,Corregido: todos los estados de vulnerabilidad se ingieren filter_superseded_qids=1: ignorar las vulnerabilidades reemplazadas Etiquetas: filtrar por etiquetas show_tags=1: recuperar etiquetas Qualys show_cloud_tags=1: recuperar etiquetas en la nube |
Una vez que los datos se transfirieron por completo, se pueden revisar en Kibana Discover (logs-* data view -> data_stream.dataset : "qualys_vmdr.asset_host_detection") ), o en la aplicación Kibana Security (Hallazgos -> vulnerabilidades).
Carga de datos en Python con el cliente de elasticsearch
Dado que el cálculo del análisis de supervivencia se realizará en Python, necesitamos extraer datos de elastic en un marco de datos de Python. Hay varias formas de lograrlo, y en este artículo nos centraremos en dos de ellas.
Con ES|QL
La forma más fácil y conveniente es aprovechar ES|QL con el formato de flecha. Completará automáticamente el marco de datos de Python (filas y columnas). Recomendamos leer la entrada del blog From ES|QL a marcos de datos nativos de Pandas en Python para obtener más detalles.
from elasticsearch import Elasticsearch
import pandas as pd
client = Elasticsearch(
"https://[host].elastic-cloud.com",
api_key="...",
)
response = client.esql.query(
query="""
FROM logs-qualys_vmdr.asset_host_detection-default
| WHERE elastic.owner.team == "platform-security" AND elastic.environment == "production"
| WHERE qualys_vmdr.asset_host_detection.vulnerability.is_ignored == FALSE
| EVAL vulnerability_age = DATE_DIFF("day", qualys_vmdr.asset_host_detection.vulnerability.first_found_datetime, qualys_vmdr.asset_host_detection.vulnerability.last_found_datetime)
| STATS
mean=AVG(vulnerability_age),
median=MEDIAN(vulnerability_age)
""",
format="arrow",
)
df = response.to_pandas(types_mapper=pd.ArrowDtype)
print(df)
Hoy en día, tenemos una limitación con ESQL: no podemos paginar a través de los resultados. Por lo tanto, estamos limitados a 10K documentos de salida (100K si se modifica la configuración del servidor). Se puede seguir el progreso a través de esta solicitud de mejora.
Con DSL
En el cliente de Python de elasticsearch, hay una característica nativa para extraer todos los datos de una consulta con paginación transparente. La parte desafiante es crear la consulta DSL. Se recomienda crear la consulta en Detectar y, a continuación, hacer clic en Inspeccionar y, a continuación, en la pestaña Aplicar para obtener la consulta de DSL.
query = {
"track_total_hits": True,
"query": {
"bool": {
"filter": [
{
"match": {
"elastic.owner.team": "awesome-sre-team"
}
},
{
"match": {
"elastic.environment": "production"
}
},
{
"match": {
"qualys_vmdr.asset_host_detection.vulnerability.is_ignored": False
}
}
]
}
},
"fields": [
"@timestamp",
"qualys_vmdr.asset_host_detection.vulnerability.unique_vuln_id",
"qualys_vmdr.asset_host_detection.vulnerability.first_found_datetime",
"qualys_vmdr.asset_host_detection.vulnerability.last_found_datetime",
"elastic.vulnerability.age",
"qualys_vmdr.asset_host_detection.vulnerability.status",
"vulnerability.severity",
"qualys_vmdr.asset_host_detection.vulnerability.is_ignored"
],
"_source": False
}
results = list(scan(
client=es,
query=query,
scroll='30m',
index=source_index,
size=10000,
raise_on_error=True,
preserve_order=False,
clear_scroll=True
))
Análisis de supervivencia
Puede consultar el código para comprenderlo o reproducirlo en el conjunto de datos.
Lo que aprendimos
Apoyándonos en la investigación del Instituto Cyentia , analizamos algunas formas diferentes de medir cuánto tiempo lleva remediar las vulnerabilidades empleando medios, medianas y curvas de supervivencia. Cada método brinda una lente diferente a través de la cual podemos comprender el tiempo para parchear los datos, y la comparación es importante porque, dependiendo del método que empleemos, sacaríamos conclusiones muy diferentes sobre qué tan bien se están abordando las vulnerabilidades.
El primer método se centra solo en las vulnerabilidades que ya se cerraron. Calcula la mediana y el tiempo medio que se tardó en parchearlos. Esto es intuitivo y simple, pero deja fuera una parte potencialmente grande e importante de los datos (las vulnerabilidades que aún están abiertas). Como resultado, tiende a subestimar el tiempo real que lleva remediar, especialmente si algunas vulnerabilidades permanecen abiertas mucho más tiempo que otras.
El segundo método intenta incluir vulnerabilidades cerradas y abiertas empleando el tiempo que estuvieron abiertas hasta ahora. Hay muchas opciones para aproximar un tiempo de parcheo para las vulnerabilidades abiertas, pero para simplificar aquí asumimos que fueron (¿serán?) parcheadas en el momento del reporte, lo cual sabemos que no es cierto. Pero ofrece una forma de tener en cuenta su existencia.
El tercer método emplea el análisis de supervivencia. Específicamente, usamos el estimador de Kaplan-Meier para modelar la probabilidad de que una vulnerabilidad aún esté abierta en un momento dado. Este método maneja las vulnerabilidades abiertas correctamente: en lugar de fingir que están parcheadas, las trata como datos "censurados". La curva de supervivencia que produce disminuye con el tiempo, mostrando la proporción de vulnerabilidades aún abiertas a medida que pasan los días o las semanas.
¿Cuánto duran las vulnerabilidades?
En la instantánea actual de 6 meses [^2], el tiempo de parche solo cerrado tiene una mediana de ~ 33 días y una media de ~ 35 días. En la superficie, eso parece razonable, pero la curva de Kaplan-Meier muestra lo que esconden esos números: a 33 días, ~ 54% todavía están abiertos; a 35 días, ~46% todavía están abiertos. Entonces, incluso alrededor de la marca "típica" de un mes, aproximadamente la mitad de los problemas siguen sin resolver.
También calculamos las estadísticas observadas hasta ahora (tratando las vulnerabilidades abiertas como si estuvieran parcheadas al final de la ventana de medición). En esta ventana resultan ser casi los mismos (mediana ~33 días, media ~35 días) porque las edades de las partidas abiertas de hoy se agrupan cerca de un mes. Esa coincidencia puede hacer que los promedios parezcan tranquilizadores, pero es incidental e inestable: si cambiamos la instantánea justo antes del impulso mensual del parche y estas mismas estadísticas caen bruscamente (vimos una mediana observada de ~ 19 días y observamos una media de ~ 15 días) sin ningún cambio en el proceso subyacente.
La curva de supervivencia evita esa trampa, porque responde a la pregunta de "% aún abierto luego de 30/60/90 días", y ofrece visibilidad de la larga cola que permanece abierta mucho más allá de un mes.
¿Parchear todo en todas partes de la misma manera?
El análisis de supervivencia estratificado lleva la idea de las curvas de supervivencia un paso más allá. En lugar de mirar todas las vulnerabilidades juntas en un gran grupo, las separa en grupos (o "estratos") en función de alguna característica significativa. En nuestro análisis, estratificamos las vulnerabilidades por gravedad, criticidad de los activos, entorno, proveedor de la nube, equipo/división/organización. Cada grupo tiene su propia curva de supervivencia, y aquí, en el gráfico de ejemplo, comparamos la rapidez con la que se remedian las diferentes gravedades de vulnerabilidad a lo largo del tiempo.
El beneficio de este enfoque es que expone diferencias que de otro modo estarían ocultas en el agregado. Si solo observamos la curva de supervivencia general, solo podemos sacar conclusiones sobre el desempeño de la remediación en todos los ámbitos. Pero la estratificación revela si los diferentes equipos, entornos o problemas de gravedad se abordan más rápido que el resto, y en nuestro caso que la estrategia de parchear todo es realmente consistente. Este nivel de detalle es importante para realizar mejoras específicas, ayudándonos a comprender no solo cuánto tiempo lleva la remediación en general, sino también si existen cuellos de botella reales y dónde.
¿Qué tan rápido actúan los equipos?
Si bien la curva de supervivencia enfatiza cuánto tiempo permanecen abiertas las vulnerabilidades, podemos cambiar la perspectiva empleando la función de distribución acumulativa (CDF) en su lugar. El CDF se centra en la rapidez con la que se parchean las vulnerabilidades, mostrando la proporción de vulnerabilidades que se remediaron en un momento dado.
Nuestra elección de trazar el CDF proporciona una imagen clara de la velocidad de remediación, sin embargo, es importante tener en cuenta que esta versión incluye solo vulnerabilidades que fueron parcheadas dentro de la ventana de tiempo observada. A diferencia de la curva de supervivencia que calculamos sobre una cohorte continua de 6 meses para capturar ciclos de vida completos, la CDF se calcula mes a mes en los elementos cerrados en ese mes [^ 3].
Como tal, nos dice qué tan rápido los equipos remedian las vulnerabilidades una vez que lo hacen, y no refleja cuánto tiempo permanecen abiertas las vulnerabilidades no resueltas. Por ejemplo, vemos que el 83,2% de las vulnerabilidades cerradas en el mes actual se resolvieron dentro de los 30 días posteriores a la primera detección. Esto resalta la velocidad de aplicación de parches para parches recientes y exitosos, pero no tiene en cuenta las vulnerabilidades de mayor duración que permanecen abiertas y es probable que tengan una duración de tiempo más larga. Por lo tanto, empleamos el CDF para comprender el comportamiento de respuesta a corto plazo, mientras que la dinámica del ciclo de vida completo viene dada por una combinación de CDF junto con el análisis de supervivencia: el CDF describe qué tan rápido actúan los equipos una vez que parchean, mientras que la curva de supervivencia muestra cuánto duran realmente las vulnerabilidades.
Diferencia entre el análisis de supervivencia y la media/mediana
Espera, dijimos que el análisis de supervivencia es mejor analizar el tiempo hasta el parche para evitar el impacto de los valores atípicos. Pero en este ejemplo, el análisis de media/mediana y supervivencia proporciona resultados similares. ¿Cuál es el valor agregado? La razón es simple: no tenemos valores atípicos en nuestros entornos de producción, ya que nuestro proceso de aplicación de parches es totalmente automatizado y efectivo.
Para demostrar el impacto en los datos heterogéneos, usaremos un ejemplo obsoleto de un entorno que no es de producción y que carece de parches automatizados.
Consulta ESQL:
FROM qualys_vmdr.vulnerability_6months
| WHERE elastic.environment == "my-outdated-non-production-environment"
| WHERE qualys_vmdr.asset_host_detection.vulnerability.is_ignored == FALSE
| EVAL vulnerability_age = DATE_DIFF("day", qualys_vmdr.asset_host_detection.vulnerability.first_found_datetime, qualys_vmdr.asset_host_detection.vulnerability.last_found_datetime)
| STATS
count=COUNT(*),
count_closed_only=COUNT(*) WHERE qualys_vmdr.asset_host_detection.vulnerability.status == "Fixed",
mean_observed_so_far=MEDIAN(vulnerability_age),
mean_closed_only=MEDIAN(vulnerability_age) WHERE qualys_vmdr.asset_host_detection.vulnerability.status == "Fixed",
median_observed_so_far=MEDIAN(vulnerability_age),
median_closed_only=MEDIAN(vulnerability_age) WHERE qualys_vmdr.asset_host_detection.vulnerability.status == "Fixed"
| Observado hasta ahora | Solo cerrado | |
|---|---|---|
| Recuento | 833 | 322 |
| Significar | 178.7 (días) | 163.8 (días) |
| Mediana | 61 (días) | 5 (días) |
| Mediana de supervivencia | 527 (días) | N/A |
En este ejemplo, el uso de la media y la mediana arroja resultados muy diferentes. Elegir una única métrica representativa puede ser un desafío y potencialmente engañoso. El gráfico de análisis de supervivencia representa con precisión nuestra eficacia para abordar las vulnerabilidades dentro de este entorno.
Consideraciones finales:
Los beneficios de usar el análisis de supervivencia provienen no solo de una medición más precisa, sino también de la información sobre la dinámica del comportamiento de los parches, que muestra dónde ocurren los cuellos de botella, los factores que afectan la velocidad de los parches y si se alinea con nuestro SLO. Desde una perspectiva de integración técnica, el uso del análisis de supervivencia como parte de nuestros flujos de trabajo operativos e reportes se puede lograr con cambios adicionales mínimos en nuestra configuración actual de Elastic Stack: el análisis de supervivencia puede ejecutar con la misma cadencia que nuestro ciclo de parches y los resultados se envían a Kibana para su visualización. El beneficio definitivo es combinar nuestras métricas operativas existentes con el análisis de supervivencia tanto para las tendencias a largo plazo como para el seguimiento del rendimiento a corto plazo.
De cara al futuro, estamos experimentando con nuevas métricas adicionales como la tasa de llegada, la tasa de evolución y la tasa de escape que nos brindan una forma de avanzar hacia una comprensión más dinámica de cómo se manejan realmente las vulnerabilidades.
La tasa de llegada es la medida de la rapidez con la que ingresan nuevas vulnerabilidades al entorno. Saber que aparecen cincuenta nuevos CVE cada mes, por ejemplo, nos dice qué esperar en la carga de trabajo incluso antes de comenzar a medir los parches. Por lo tanto, la tasa de llegada es una métrica que no necesariamente informa sobre el retraso, sino más bien sobre la presión aplicada al sistema.
La tasa de evolución (tendencia) muestra la otra mitad de la ecuación: la rapidez con la que se remedian las vulnerabilidades en relación con la rapidez con la que llegan.
Escape Rate agrega otra dimensión al enfocar en vulnerabilidades que se deslizan más allá de los puntos donde deberían ser contenidas. En nuestro contexto, un escape se trata de CVE que no detectan ventanas de parches o superan los umbrales de SLO. Una tasa de escape elevada no solo muestra que existen vulnerabilidades, sino que también muestra que el proceso diseñado para controlarlas está fallando, ya sea porque los ciclos de parches son demasiado lentos, faltan procesos de automatización o los controles de compensación no funcionan según lo previsto.
Juntas, las métricas crean una mejor imagen: la tasa de llegada nos dice cuánto riesgo nuevo se está introduciendo; Las tendencias de quema muestran si estamos manteniendo el ritmo de esa presión o si estamos abrumados por ella; Las tasas de escape exponen dónde persisten las vulnerabilidades a pesar de los controles planeados.
[1]: Un valor atípico en estadística es un punto de datos que está muy lejos de la tendencia central (o lejos del resto de los valores de un conjunto de datos). Por ejemplo, si la mayoría de las vulnerabilidades se parchean en 30 días, pero una tarda 600 días, ese caso de 600 días es un valor atípico. Los valores atípicos pueden subir o bajar los promedios de maneras que no reflejan la experiencia "típica". En el contexto de la aplicación de parches, estas son las vulnerabilidades especialmente lentas de parchear que permanecen abiertas mucho más tiempo de lo normal. Pueden representar situaciones raras pero importantes, como sistemas que no se pueden actualizar fácilmente o parches que requieren pruebas exhaustivas.
[2]: Nota: El conjunto de datos actual de 6 meses incluye tanto todas las vulnerabilidades que permanecen abiertas al final del periodo de observación (independientemente de cuánto tiempo hace que se abrieron / se vieron por primera vez) como todas las vulnerabilidades que se cerraron durante la ventana de 6 meses. A pesar de este enfoque de cohorte mixto, las curvas de supervivencia de las ventanas de observación anteriores muestran tendencias consistentes, particularmente en la primera parte de la curva. La forma y la pendiente durante los primeros 30 a 60 días demostraron ser notablemente estables en todas las instantáneas, lo que sugiere que métricas como el tiempo medio para parchear y el comportamiento de corrección en etapa temprana no son artefactos de la ventana de observación corta. Si bien las estimaciones a largo plazo (p. ej. percentil 90) permanecen incompletas en instantáneas más cortas, las conclusiones extraídas de estas cohortes aún reflejan dinámicas de parches persistentes y confiables.
[3]: Mantuvimos el CDF en una cadencia mensual para los reportes operativos (rendimiento y cumplimiento de SLO para el trabajo completado durante el mes en curso), mientras que Kaplan-Meier emplea una ventana de 6 meses para manejar adecuadamente la censura y exponer el riesgo de cola en toda la cohorte más amplia.
