Primero, clona el repositorio en tu máquina local:
git clone https://github.com/MateoRamirezRubio1/crm_vulnerabilities_challenge.git
cd crm_vulnerabilities_challenge
cd crm_vulnerabilities
Para ejecutar la aplicación con Docker Compose, asegúrate de estar en la raíz del proyecto (donde está el archivo docker-compose.yml) y ejecuta el siguiente comando:
docker-compose up --build
Esto realizará las siguientes acciones:
Construir la imagen Docker definida en el Dockerfile. Inicia el servidor de desarrollo de Django en el puerto 8000.
Para ejecutar el proyecto sin reconstruir las imágenes (esto es útil después de la primera ejecución):
docker-compose up
El equipo de seguridad está desarrollando un sistema para gestionar la seguridad de los diferentes sistemas que se encuentran desplegados en la infraestructura Cloud mediante el cruce de información con los CVEs del NIST. El objetivo de la aplicación permitirnos obtener un listado de vulnerabilidades del NIST (https://nvd.nist.gov/developers/vulnerabilities). Adicionalmente, esta aplicación debe ofrecer la posibilidad de indicarle qué vulnerabilidades ya se encuentran fixeadas en nuestra infraestructura y que NO queremos que aparezcan en el listado. En concreto, se debe desarrollar una API REST (con sus convenciones) que tenga los siguientes métodos:
- Endpoint GET que devuelve el listado total de las vulnerabilidades.
- Endpoint POST que reciba la/s vuln/s fixeada/s.
- Endpoint GET que devuelva el listado de vulnerabilidades exceptuando las fixeadas (ingresadas en el endpoint del punto 2).
- Endpoint GET que permita obtener información sumarizada de vulnerabilidades por severidad.
Consideraciones
- La base de datos a utilizar queda a elección. - Lenguaje Python - Usar Django REST framework. - La aplicación desarrollada debe poder ejecutarse dockerizada.
Esta guía explica cómo interactuar con los diferentes endpoints de la APIs para gestionar vulnerabilidades, alertas y usuarios. A través de estos pasos, puedes obtener vulnerabilidades desde la base de datos NIST, marcarlas como corregidas y recibir notificaciones por correo. También veremos cómo registrar, autenticar y administrar usuarios.
GET: http://127.0.0.1:8000/v1/vulnerabilities/
Este endpoint te permite obtener una lista de todas las vulnerabilidades registradas en la base de datos de NIST. La respuesta incluye información relevante, como el ID de la vulnerabilidad (CVE), una descripción y su severidad.
POST: http://127.0.0.1:8000/v1/vulnerabilities/fixed/
Para marcar una vulnerabilidad como corregida, envía la información correspondiente en un formato JSON. Aquí se indica que la vulnerabilidad CVE-1999-0095 ha sido corregida.
Entrada JSON de ejemplo:
{
"id": "CVE-1999-0095",
"description": "The debug command in Sendmail is enabled, allowing attackers to execute commands as root.",
"severity": "HIGH"
}
GET: http://127.0.0.1:8000/v1/vulnerabilities/unfixed/
Este endpoint devuelve una lista de vulnerabilidades que aún no han sido marcadas como corregidas. Si consultas este endpoint después de marcar una vulnerabilidad como corregida, esta ya no aparecerá en la lista.
Por ejemplo, si acabas de corregir CVE-1999-0095, ya no debería figurar en los resultados, pero sí las demas vulnerabilidades
Este comportamiento es importante porque confirma que la acción de marcación ha tenido efecto.
GET: http://127.0.0.1:8000/v1/vulnerabilities/summary/
Este endpoint proporciona un resumen general de todas las vulnerabilidades, agrupadas según su severidad (por ejemplo, HIGH, MEDIUM, LOW). Te permite ver cuántas vulnerabilidades existen en cada categoría.
El resumen es útil para comprender la gravedad general de las vulnerabilidades en el sistema y priorizar las correcciones.
GET: http://127.0.0.1:8000/v1/alerts/
Cuando se marca una vulnerabilidad como corregida, se genera una alerta o notificación que se envía al destinatario especificado. En este caso, una alerta será enviada notificando que la vulnerabilidad CVE-1999-0095 ha sido corregida. Puedes consultar todas las alertas utilizando este endpoint.
Las alertas se envían dependiendo de la severidad de la vulnerabilidad fixeada por un medio de notificación diferene, por ejemplo para la vulnerabilidad fixeada que ingresamos cuya severidad es HIGH sera enviada la alerta por correo electrónico:
POST: http://127.0.0.1:8000/v1/auth/register/
Para registrar un nuevo usuario, se envían detalles como el nombre de usuario, la contraseña y el correo electrónico. En este ejemplo, estamos registrando un usuario con rol de administrador.
Entrada JSON:
{
"username": "prueba",
"password": "123",
"email": "[email protected]",
"role": "admin"
}
Después de realizar esta acción, el sistema confirmará que el usuario se ha registrado con éxito.
POST: http://127.0.0.1:8000/v1/auth/login/
Este endpoint es para iniciar sesión en el sistema. Debes enviar el nombre de usuario y la contraseña que se utilizaron al registrarse. El sistema te devolverá un token de acceso y un token de refresco que puedes usar para futuras solicitudes autenticadas.
Entrada JSON:
{
"username": "prueba",
"password": "123"
}
El token de acceso te permite realizar solicitudes protegidas, mientras que el token de refresco sirve para obtener un nuevo token de acceso cuando expire.
POST: http://127.0.0.1:8000/v1/auth/refresh/
Cuando el token de acceso expira, puedes usar este endpoint para obtener uno nuevo usando el token de refresco. Esto asegura que el usuario no tenga que iniciar sesión nuevamente. El token de acceso debe ser dado por el JWT Bearer y en el cuerdo de la solicitud el token de refresh:
Entrada JSON:
{
"refresh": "token_de_refresh_aquí"
}
El sistema devolverá un nuevo token de acceso que puedes usar en futuras solicitudes.
POST: http://127.0.0.1:8000/v1/auth/logout/
Para cerrar sesión, utiliza este endpoint. Debes enviar el token de refresco junto con el token de acceso en el encabezado de la solicitud. Esto revocará el token, y si intentas usarlo después de cerrar sesión, recibirás un error de "Token is blacklisted". El token de acceso debe ser dado por el JWT Bearer y en el cuerdo de la solicitud el token de refresh:
Entrada JSON:
{
"refresh": "token_de_refresh_aquí"
}
Esto garantiza que no puedas utilizar tokens que ya han sido revocados, añadiendo una capa extra de seguridad.
El proyecto implementa un rate limiter o limitador de tasa que restringe el número de peticiones que se pueden realizar a la API. Este rate limiter ha sido configurado para permitir un máximo de 15 peticiones por minuto, lo que previene que un solo cliente realice demasiadas solicitudes en un corto periodo, evitando una sobrecarga en la API.
Cuando se supera este límite, el cliente recibe una respuesta de error (429 Too Many Requests), indicando que deberá esperar antes de realizar más solicitudes. Este mecanismo protege tanto el servidor como el cliente al regular el flujo de datos y asegurar un acceso justo para todos los usuarios.
En el proyecto, se implementó el uso de logging para mejorar la visibilidad y el rastreo de los eventos y errores dentro de la aplicación. Este enfoque nos permite registrar información clave sobre la ejecución del código, como la recuperación de datos desde una API externa o el estado de la caché. Gracias a los logs, podemos monitorizar la aplicación de manera más efectiva, identificar y diagnosticar problemas rápidamente y mantener un registro detallado de las operaciones realizadas, lo cual es fundamental para el mantenimiento y la depuración del sistema. El uso de logging contribuye significativamente a la robustez y la transparencia del proyecto.
El módulo de vulnerabilities
gestiona las vulnerabilidades del sistema, permitiendo la obtención, almacenamiento y resumen de las mismas. Además, se divide en varias capas de funcionalidad, como servicios, serializadores, observadores, clientes y especificaciones, siguiendo principios de arquitectura limpia y patrones de diseño que hacen que el sistema sea extensible y fácil de mantener.
Contiene la clase NVDClient
, que se encarga de interactuar con fuentes externas, como la National Vulnerability Database (NVD), para recuperar información relacionada con las vulnerabilidades del sistema.
- Patrón utilizado: Cliente Externo (External Client), que abstrae la comunicación con un servicio externo (NVD).
Este directorio contiene la implementación del patrón de diseño Observer. En este caso, el VulnerabilityObserver
observa los cambios en las vulnerabilidades y actúa en consecuencia, como crear alertas cuando se ingresa una nueva vulnerabilidad fixeada.
- Patrón utilizado: Observer, utilizado para suscribirse a cambios en el estado de vulnerabilidades.
El repositorio almacena las vulnerabilidades y proporciona una interfaz abstracta para las operaciones de base de datos. Permite el acceso y manejo de datos de forma desacoplada de la lógica del negocio.
- Patrón utilizado: Repositorio (Repository), utilizado para encapsular el acceso a la base de datos.
Este directorio contiene varios serializadores que convierten los objetos del modelo de vulnerabilidad en datos que pueden ser enviados a través de la API. Aquí se definen:
-
FixedVulnerabilitySerializer
: Serializa vulnerabilidades que ya han sido solucionadas. -
NISTVulnerabilitySerializer
: Serializa vulnerabilidades según los estándares de la NIST. -
VulnerabilitySummarySerializer
: Proporciona un resumen de las vulnerabilidades. -
Patrón utilizado: Adapter/Bridge, para la transformación de objetos entre la capa de datos y la API.
Define la lógica de negocio principal a través del VulnerabilityService
, que gestiona todas las acciones relacionadas con las vulnerabilidades. Además, se utiliza el ServiceFactory
para crear instancias de los servicios requeridos e inyección de dependencias.
- Patrón utilizado:
- Servicio (Service Layer) para encapsular la lógica del negocio relacionada con vulnerabilidades.
Define las reglas de filtrado de vulnerabilidades no solucionadas a través del patrón Specification, lo que facilita agregar nuevas reglas de negocio sin modificar el código existente.
- Patrón utilizado: Specification, utilizado para encapsular reglas de negocio reutilizables como en el caso saber si una vulnerabilidad ya ha sido fixeada.
Contiene las vistas basadas en clases que exponen la API para la gestión de vulnerabilidades:
VulnerabilityListView
: Devuelve la lista completa de vulnerabilidades.UnfixedVulnerabilitiesView
: Muestra vulnerabilidades que no han sido solucionadas.VulnerabilityFixedView
: Muestra vulnerabilidades que ya han sido solucionadas.VulnerabilitySummaryView
: Proporciona un resumen de las vulnerabilidades por tipo.
El módulo proporciona los siguientes endpoints para interactuar con las vulnerabilidades:
GET /api/vulnerabilities/
: Obtiene la lista completa de vulnerabilidades.GET /api/vulnerabilities/unfixed/
: Obtiene la lista de vulnerabilidades que no han sido solucionadas.GET /api/vulnerabilities/fixed/
: Obtiene la lista de vulnerabilidades solucionadas.POST /api/vulnerabilities/fixed/
: Permite ingresar y crear una nueva vulneravilidad que ha sido fixeada.GET /api/vulnerabilities/summary/
: Obtiene un resumen de las vulnerabilidades (solucionadas y no solucionadas).
El cliente de la API de NVD (National Vulnerability Database) ha sido diseñado para optimizar las solicitudes utilizando caché en memoria. Cada vez que se solicita la lista de vulnerabilidades, el cliente primero verifica si los datos previamente obtenidos siguen siendo válidos, basándose en un tiempo de expiración definido (CACHE_TIMEOUT
de 3600 segundos o 1 hora).
- Almacena el resultado de la consulta a la API en una variable privada (
_vulnerabilities_cache
) y registra el tiempo de la última actualización. - Antes de realizar una nueva solicitud, verifica si los datos en cache aún son válidos mediante el método
_is_cache_valid()
, comparando el tiempo actual con el de la última actualización. - Si el cache ha expirado o no existe, realiza una nueva solicitud a la API y actualiza el cache.
- Si el cache es válido, devuelve los datos almacenados, mejorando así la eficiencia y reduciendo la cantidad de llamadas a la API.
Este mecanismo de cache permite reducir la latencia de las consultas y evitar el exceso de solicitudes a la API de NIST, especialmente cuando se manejan grandes volúmenes de datos.
Si los datos en cache siguen siendo válidos, se devolverán sin necesidad de realizar una nueva consulta al servidor.
El módulo de alerts
está diseñado para gestionar las alertas generadas a partir de vulnerabilidades u otros eventos relevantes. Implementa un sistema de notificaciones que utiliza múltiples canales de comunicación, como correos electrónicos, SMS, notificaciones push, y Slack. Este módulo sigue patrones de diseño, incluyendo el patrón Observer, Factory, y Singleton, para gestionar eficientemente la creación, distribución y observación de alertas.
Este directorio contiene el Subject del patrón Observer: AlertSubject
. Aquí se define el comportamiento que permite suscribir a diferentes observadores para reaccionar ante cambios, como la creación de una nueva alerta.
- Patrón utilizado: Observer, con
AlertSubject
gestionando la suscripción de observadores y distribuyendo notificaciones de nuevas alertas.
Contiene el repositorio de alertas (AlertsRepository
), que maneja el acceso a los datos relacionados con las alertas y sus detalles.
- Patrón utilizado: Repository, para encapsular las operaciones de acceso a la base de datos y desacoplar la lógica de negocio de la lógica de persistencia.
Incluye el serializador ListAlertSerializer
, el cual convierte las alertas en un formato que puede ser enviado a través de la API.
- Patrón utilizado: Adapter, ya que transforma las entidades del sistema en datos listos para ser consumidos por la API.
Esta subcarpeta contiene los servicios de la Fábrica de Alertas. Se utiliza para crear y despachar alertas a través de múltiples canales.
-
AlertFactory
: Es responsable de crear diferentes tipos de alertas. -
AlertDispatcher
: Se encarga de despachar las alertas generadas a los canales correspondientes. -
EmailAlertService
,PushAlertService
,SlackAlertService
,SMSAlertService
: Estos servicios representan las alertas enviadas a través de diferentes medios de comunicación. -
Patrón utilizado:
- Factory para la creación de diferentes tipos de alertas según la severidad de la vulnerabilidad (Al momento de ingresar una nueva vulnerabilidad por medio del POST de la app vulnerabilities, dependiendo de la severidad de la vulnerabilidad ingresada se envía un tipo de alerta u otra).
En esta subcarpeta se define AlertService
, que gestiona la lógica de negocio de las alertas desde el punto de vista de la API.
Contiene las vistas basadas en clases que exponen la API para la gestión de alertas:
-
AlertListView
: Devuelve la lista completa de alertas registradas en el sistema. -
Patrón utilizado: View Layer de Django, utilizando vistas basadas en clases.
El módulo expone el siguiente endpoint para interactuar con las alertas:
GET /api/alerts/
: Obtiene la lista completa de alertas.
El módulo de authentication
proporciona funcionalidades para la autenticación de usuarios utilizando JWT (JSON Web Tokens). Este módulo permite a los usuarios registrarse, iniciar sesión, y manejar la autorización mediante diferentes roles (admin, advanced, basic). Los permisos son gestionados a través de clases personalizadas de permisos que definen qué tipo de usuario puede acceder a ciertas vistas.
Contiene el modelo de usuario extendido, que incluye campos adicionales como role
para manejar roles personalizados (admin
, advanced
, basic
).
- Roles de usuario:
admin
: Administrador con acceso completo a todas las funcionalidades.advanced
: Usuario avanzado con permisos adicionales.basic
: Usuario básico con acceso limitado.
Define permisos personalizados para los diferentes roles. Cada clase de permiso (IsAdmin
, IsAdvancedUser
, IsBasicUser
) comprueba el rol del usuario que realiza la solicitud.
- Permisos personalizados:
IsAdmin
: Solo usuarios con rol deadmin
.IsAdvancedUser
: Solo usuarios con rol deadvanced
.IsBasicUser
: Solo usuarios con rol debasic
.
Encapsula la lógica de acceso a la base de datos relacionada con la autenticación de usuarios. Gestiona las consultas a la base de datos para la creación de usuarios.
Define los serializadores que convierten los datos de los usuarios en JSON y viceversa. También se utiliza para validar los datos de registro e inicio de sesión.
RegisterSerializer
: Gestiona el registro de nuevos usuarios.CustomTokenObtainPairSerializer
: Personaliza el token JWT, incluyendo información adicional como el rol del usuario en la respuesta.
Incluye la lógica de negocio para el registro de usuarios.
Define las rutas para las vistas relacionadas con la autenticación. Las rutas disponibles son:
/login/
: Iniciar sesión y obtener el token JWT./register/
: Registrar un nuevo usuario./logout/
: Hacer logout y añadir el refresh token a la lista negra./refresh/
: Obtener un nuevo access token mediante el refresh token.
Define las vistas que manejan las solicitudes HTTP relacionadas con la autenticación:
CustomTokenObtainPairView
: Vista para obtener los tokens JWT (access y refresh) al iniciar sesión.RegisterView
: Vista para registrar nuevos usuarios.LogoutAndBlacklistRefreshTokenView
: Vista para hacer logout y añadir el token de refresh a la lista negra.
El módulo expone los siguientes endpoints para la autenticación y manejo de roles:
POST /api/authentication/login/
: Inicia sesión y devuelve elaccess
yrefresh
tokens.POST /api/authentication/register/
: Registra un nuevo usuario.POST /api/authentication/logout/
: Añade elrefresh
token a la lista negra y cierra sesión.POST /api/authentication/refresh/
: Renueva elaccess
token utilizando elrefresh
token.
El sistema de permisos está basado en roles de usuario, definidos en el modelo de usuario. Los permisos están gestionados por las clases de permisos personalizados:
- IsAdmin: Solo permite el acceso a usuarios con rol
admin
. - IsAdvancedUser: Solo permite el acceso a usuarios con rol
advanced
. - IsBasicUser: Solo permite el acceso a usuarios con rol
basic
.