Portafolio profesional

Construyendo SentinelAI: Un Sistema Híbrido de Seguridad Web

Cómo construí un sistema demo que combina reglas expertas, machine learning y LLMs para detectar comportamientos sospechosos en aplicaciones web.

Artículo

# Construyendo SentinelAI: Un Sistema Híbrido de Seguridad Web ## El problema Cuando trabajás en aplicaciones web modernas, te encontrás con un problema recurrente: ¿cómo distinguir entre un usuario legítimo y uno malicioso? Las herramientas tradicionales de seguridad suelen generar alertas sin contexto, dejando a los equipos con falsos positivos que consumen horas de investigación. En muchos proyectos donde trabajé, veía cómo los desarrolladores implementaban reglas estáticas (`if request_count > 100: alert()`) que no capturaban comportamientos sofisticados ni daban explicaciones útiles. Necesitaba algo que combinara múltiples señales-ubicación, frecuencia, patrones de navegación, acceso a recursos-y produjera alertas con scoring ponderado y explicaciones en lenguaje natural. ## Por qué un enfoque híbrido Podría haber usado solo machine learning, pero los modelos ML puros requieren datasets enormes y etiquetados-algo difícil en seguridad donde los datos reales de ataques son escasos. También podría haber usado solo reglas expertas, pero eso reproduce los mismos problemas de falsos positivos. Elegí un enfoque híbrido por tres razones concretas: 1. **Las reglas expertas** capturan conocimiento de dominio inmediato ("si un usuario descarga 50 archivos en 5 minutos, es sospechoso") 2. **El machine learning** detecta patrones que las reglas no anticipan 3. **La detección de anomalías** identifica desviaciones estadísticas del comportamiento normal FastAPI fue la elección natural para el backend porque necesitaba algo rápido para prototipar el pipeline de evaluación en tiempo real, con validación fuerte (Pydantic) y autenticación JWT lista. React con Vite me dio el frontend unificado donde podía tener tanto la aplicación demo (que genera telemetría) como el dashboard admin (que consume alertas) en el mismo códigobase. ## Arquitectura El flujo de datos en SentinelAI sigue un pipeline claro desde la generación de eventos hasta la visualización de alertas: ```mermaid flowchart LR Frontend["React + Vite App de usuario / Dashboard"] -- eventos HTTP --> API["FastAPI Backend endpoints"] API -- almacena --> DB["Base de datos SQLAlchemy + Alembic"] API -- evalúa --> Engine["Motor de seguridad Sistema experto + ML"] Engine -- consulta --> DB Engine -- genera --> Alertas["Alertas Scoring ponderado"] Alertas -- notifica --> Email["Servicio de email Notificaciones"] Alertas -- explica --> LLM["Servicio LLM Explicaciones"] Frontend -- consulta --> API ``` **Decisiones arquitectónicas clave:** 1. **Frontend unificado**: En vez de tener dos aplicaciones separadas (app demo + dashboard), construí una sola SPA con rutas diferenciadas (`/` para la app demo, `/admin/*` para el dashboard). Esto simplifica el deployment y permite compartir componentes UI mediante Atomic Design. 2. **Dominio primero**: Estructuré el backend con Domain-Driven Design (`src/domain/entities/`, `src/domain/value_objects/`) para separar claramente la lógica de negocio de la infraestructura. El motor de seguridad vive en su propio módulo, no mezclado con la API. 3. **Pipeline modular de evaluación**: El motor de seguridad tiene tres componentes intercambiables: sistema experto (reglas), clasificador ML, y detector de anomalías. Cada uno produce un score parcial que luego se combina en un score ponderado final. 4. **LLM como servicio, no como core**: En vez de hacer que el LLM tome decisiones de seguridad (riesgoso y lento), lo uso solo para generar explicaciones naturales de alertas ya determinadas. El prompt está en `backend/src/llm/prompts/alert_explanation.txt`. ## El trade-off que nadie te cuenta El mayor trade-off vino con el dataset sintético. Para entrenar los modelos ML sin datos reales de ataques, tuve que generar datos sintéticos con `backend/data/synthetic/generator.py`. El problema: los modelos aprendían los patrones artificiales de mi generador, no comportamientos reales. Cuando probaba con datos más orgánicos, la precisión caía drásticamente. La solución fue doble. Primero, implementé **data augmentation** variando los parámetros de generación (introduciendo ruido, cambiando distribuciones). Segundo, agregué un **mecanismo de feedback** en el dashboard admin donde podía marcar alertas como falsos positivos/negativos, creando un dataset de correcciones que podía usar para re-entrenar. No era perfecto, pero mejoraba con el tiempo. ## Resultado - **Pipeline completo funcionando**: Desde evento HTTP hasta alerta en dashboard en <2 segundos, incluyendo evaluación híbrida y generación de explicación por LLM. - **Scoring de 4 niveles**: Las alertas se categorizan en bajo, medio, alto y crítico con pesos combinados de los tres sistemas (ej: reglas: 0.4, ML: 0.4, anomalías: 0.2). - **Explicaciones naturales**: Cada alerta incluye una explicación generada por LLM como "Esta alerta se generó porque el usuario accedió desde una nueva ubicación geográfica mientras realizaba descargas frecuentes de recursos sensibles, combinación que ocurre en solo el 2% de las sesiones normales." - **Dashboard integrado**: El equipo admin puede ver eventos, alertas y métricas sin salir de la misma aplicación donde ocurren los eventos demo. ## Lo que haría diferente Si empezara de nuevo, separaría más claramente el módulo de generación de datos sintéticos del de entrenamiento ML. Los mezclé por conveniencia inicial, pero eso hizo difícil después probar con datasets externos o reales. Crearía un `DataService` abstracto con implementaciones para datos sintéticos, reales, y de prueba-patrón de estrategia que hubiera dado más flexibilidad. ¿Querés profundizar en algún componente? Contactame o revisá el código en https://github.com/tu-usuario/sentinelai.