Intermedion8nautomatizaciónSOARworkflowsIOC enrichmentlow-code

n8n para Seguridad: Workflows de Automatización sin Código

Automatización de operaciones de seguridad con n8n: cinco workflows prácticos para SOC (enriquecimiento de IOCs, triaje de phishing, digest de threat feeds, notificación de vulnerabilidades y creación de tickets de incidentes). Comparativa con SOAR tradicional.

MalwareIntel Research··12 min lectura

Por qué n8n para equipos de seguridad

Los equipos de seguridad tienen un problema de automatización. Las tareas repetitivas consumen la mayor parte del tiempo de los analistas: enriquecer IOCs, triagear alertas de phishing, recopilar feeds de amenazas, notificar vulnerabilidades y crear tickets. Las plataformas SOAR (Security Orchestration, Automation and Response) resuelven esto, pero cuestan decenas de miles de euros al año y requieren meses de implementación.

n8n es una alternativa: una plataforma de automatización de workflows open source, self-hosted, con interfaz visual y capacidad de integración con cualquier API. No es un SOAR, pero cubre el 80% de las necesidades de automatización de un SOC pequeño o mediano a una fracción del coste.

n8n vs SOAR: diferencias clave

Aspecton8nSOAR enterprise
CosteGratis (self-hosted) o desde 20 EUR/mesDesde 50.000 EUR/año
DespliegueDocker en cualquier VPS (30 minutos)Semanas o meses
Integraciones securityVia HTTP Request + Code nodesNativas (cientos de conectores)
Gestión de casosNo nativa (integra con TheHive, Jira)Incluida
Aprobación humana (HITL)Via webhook + Slack/emailNativa con UI dedicada
Dashboards SOCNo nativo (integra con Grafana)Incluidos
Curva de aprendizajeBaja (interfaz visual)Alta (configuración compleja)
EscalabilidadBuena (modo queue + workers)Enterprise-grade

n8n es la opción para equipos que necesitan automatizar ya, con presupuesto limitado y sin un equipo dedicado de implementación SOAR.

Despliegue de n8n para seguridad

El despliegue recomendado para uso en seguridad es self-hosted con Docker, en un servidor dentro de tu red (o VPS privado). Los datos de seguridad no deben pasar por servicios cloud de terceros si no es estrictamente necesario.

# docker-compose.yml para n8n en seguridad
version: "3.8"

services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n-security
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
      - N8N_HOST=n8n.internal.company.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.internal.company.com/
      - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=Europe/Madrid
      - N8N_METRICS=true
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - security-net

volumes:
  n8n_data:

networks:
  security-net:
    driver: bridge

Una vez desplegado, accede a la interfaz web y configura las credenciales de las APIs que vas a usar. n8n las almacena cifradas en su base de datos interna.

Workflow 1: Pipeline de enriquecimiento de IOCs

Este workflow recibe un IOC (IP, hash, dominio) via webhook, lo consulta en múltiples fuentes de threat intelligence y devuelve un informe consolidado.

Trigger: Webhook (POST con JSON {"ioc": "valor", "type": "ip|hash|domain"})

Estructura del workflow

  1. Webhook Trigger: recibe el IOC
  2. Switch node: determina el tipo de IOC
  3. HTTP Request nodes (en paralelo):
    • VirusTotal API
    • AbuseIPDB (para IPs)
    • URLhaus (para dominios/URLs)
    • MalwareBazaar (para hashes)
    • Shodan (para IPs)
  4. Code node: consolidar resultados y calcular score
  5. IF node: si score es mayor de 70, crear alerta
  6. Slack/Email node: notificar resultado

Nodo Code: consolidación de resultados

// Consolidar resultados de multiples fuentes
const results = {
  ioc: $input.first().json.ioc,
  type: $input.first().json.type,
  timestamp: new Date().toISOString(),
  sources: {},
  risk_score: 0,
  verdict: "clean",
};

// VirusTotal
const vt = $('VirusTotal').first()?.json;
if (vt?.data?.attributes?.last_analysis_stats) {
  const stats = vt.data.attributes.last_analysis_stats;
  results.sources.virustotal = {
    malicious: stats.malicious || 0,
    suspicious: stats.suspicious || 0,
    harmless: stats.harmless || 0,
  };
  results.risk_score += Math.min(stats.malicious * 5, 40);
}

// AbuseIPDB
const abuse = $('AbuseIPDB').first()?.json;
if (abuse?.data) {
  results.sources.abuseipdb = {
    confidence_score: abuse.data.abuseConfidenceScore || 0,
    total_reports: abuse.data.totalReports || 0,
    country: abuse.data.countryCode || "",
  };
  results.risk_score += Math.min(
    abuse.data.abuseConfidenceScore / 2, 30
  );
}

// URLhaus
const urlhaus = $('URLhaus').first()?.json;
if (urlhaus?.query_status === "listed") {
  results.sources.urlhaus = {
    status: "listed",
    threat: urlhaus.threat || "unknown",
    tags: urlhaus.tags || [],
  };
  results.risk_score += 40;
}

// MalwareBazaar
const mb = $('MalwareBazaar').first()?.json;
if (mb?.query_status === "hash_found") {
  results.sources.malwarebazaar = {
    status: "known_malware",
    signature: mb.data?.[0]?.signature || "",
    file_type: mb.data?.[0]?.file_type || "",
  };
  results.risk_score += 50;
}

// Determinar veredicto
results.risk_score = Math.min(results.risk_score, 100);
if (results.risk_score >= 70) {
  results.verdict = "malicious";
} else if (results.risk_score >= 30) {
  results.verdict = "suspicious";
} else {
  results.verdict = "clean";
}

return [{ json: results }];

Este workflow se invoca desde cualquier herramienta que pueda hacer un POST HTTP. Un analista puede integrarlo en un script, en un chatbot de Slack o en otro workflow de n8n.

Workflow 2: Triaje automático de alertas de phishing

Cuando un usuario reporta un email sospechoso (via botón de Outlook, reenviando a [email protected] o enviando a un canal de Slack), este workflow automatiza el triaje inicial.

Trigger: IMAP (monitoriza el buzón [email protected]) o Webhook (desde un botón de reporte)

Flujo paso a paso

  1. IMAP Trigger: detecta nuevo email en el buzón de phishing
  2. Code node: extraer URLs del body del email
  3. Split In Batches: procesar cada URL
  4. HTTP Request: consultar URLhaus y VirusTotal por cada URL
  5. Code node: extraer adjuntos y calcular hashes SHA256
  6. HTTP Request: consultar MalwareBazaar por cada hash
  7. Code node: calcular score total y generar veredicto
  8. IF node: ramificar según veredicto
    • Malicioso: crear caso en TheHive + notificar Slack + bloquear sender
    • Sospechoso: notificar analista para revisión manual
    • Limpio: responder automáticamente al usuario

Nodo Code: extracción de URLs

// Extraer URLs del body del email
const body = $input.first().json.textPlain || "";
const html = $input.first().json.html || "";
const combined = body + " " + html;

// Regex para URLs
const urlRegex = /https?:\/\/[^\s<>"')\]}{,]+/g;
const urls = [...new Set(combined.match(urlRegex) || [])];

// Limpiar URLs (remover puntos finales, etc.)
const cleanUrls = urls.map(url => url.replace(/[.)]+$/, ""));

return cleanUrls.map(url => ({
  json: { url, source_email: $input.first().json.from }
}));

Workflow 3: Digest diario de threat feeds

Un resumen diario de los feeds de amenazas más relevantes, enviado por email o Slack cada mañana antes del inicio del turno.

Trigger: Cron (todos los días laborables a las 07:00)

Estructura

  1. Cron Trigger: 07:00 L-V
  2. HTTP Request (en paralelo):
    • CISA KEV (nuevas vulnerabilidades explotadas)
    • MalwareBazaar (últimas 24h, top familias)
    • URLhaus (URLs añadidas últimas 24h)
    • Feodo Tracker (nuevos C2 activos)
  3. Code node: consolidar y formatear digest
// Formatear digest diario de threat feeds
const kev = $('CISA_KEV').first()?.json?.vulnerabilities || [];
const mb = $('MalwareBazaar').first()?.json?.data || [];
const urlhaus = $('URLhaus').first()?.json?.urls || [];
const feodo = $('Feodo').first()?.json?.data || [];

const today = new Date().toISOString().split('T')[0];

let digest = `# Threat Intelligence Digest — ${today}\n\n`;

// CISA KEV
const newKev = kev.filter(v =>
  v.dateAdded === today
);
digest += `## CISA KEV (${newKev.length} nuevas)\n`;
for (const vuln of newKev.slice(0, 10)) {
  digest += `- **${vuln.cveID}**: ${vuln.vulnerabilityName}`;
  digest += ` (${vuln.vendorProject})\n`;
}
digest += "\n";

// MalwareBazaar
const families = {};
for (const sample of mb.slice(0, 100)) {
  const sig = sample.signature || "unknown";
  families[sig] = (families[sig] || 0) + 1;
}
const topFamilies = Object.entries(families)
  .sort((a, b) => b[1] - a[1])
  .slice(0, 10);

digest += `## MalwareBazaar (${mb.length} samples 24h)\n`;
digest += "| Familia | Samples |\n|---------|--------|\n";
for (const [name, count] of topFamilies) {
  digest += `| ${name} | ${count} |\n`;
}
digest += "\n";

// URLhaus
digest += `## URLhaus (${urlhaus.length} URLs nuevas)\n`;
const urlThreats = {};
for (const url of urlhaus) {
  const threat = url.threat || "unknown";
  urlThreats[threat] = (urlThreats[threat] || 0) + 1;
}
for (const [threat, count] of Object.entries(urlThreats)) {
  digest += `- ${threat}: ${count} URLs\n`;
}
digest += "\n";

// Feodo Tracker
const activeC2 = feodo.filter(c => c.status === "online");
digest += `## Feodo Tracker (${activeC2.length} C2 activos)\n`;
for (const c2 of activeC2.slice(0, 5)) {
  digest += `- ${c2.ip_address}:${c2.port}`;
  digest += ` (${c2.malware || "unknown"})\n`;
}

return [{ json: { digest, subject: `Threat Digest ${today}` } }];
  1. Email/Slack node: enviar el digest formateado

Este workflow garantiza que el equipo comienza cada día con visibilidad sobre las amenazas activas, sin que nadie tenga que revisar feeds manualmente.

Workflow 4: Notificación de resultados de escaneo de vulnerabilidades

Cuando tu escáner de vulnerabilidades (Nessus, OpenVAS, Qualys) completa un escaneo, este workflow procesa los resultados y notifica a los equipos responsables.

Trigger: Webhook (el escáner envía resultados al completar) o Cron (polling periódico a la API del escáner)

Estructura

  1. Webhook/Cron Trigger: recibir o consultar resultados
  2. Code node: filtrar vulnerabilidades por severidad
  3. IF node: separar críticas y altas del resto
  4. HTTP Request: buscar en CISA KEV si la CVE está activamente explotada
  5. Code node: priorizar por explotación activa + CVSS
  6. Slack node: notificar vulnerabilidades críticas al canal de seguridad
  7. Jira/ServiceNow node: crear tickets para remediación
  8. Email node: enviar resumen al responsable del sistema
// Filtrar y priorizar vulnerabilidades
const vulns = $input.all().map(item => item.json);

const critical = vulns.filter(v =>
  v.severity === "critical" || v.cvss >= 9.0
);
const high = vulns.filter(v =>
  v.severity === "high" || (v.cvss >= 7.0 && v.cvss < 9.0)
);

// Priorizar las que estan en CISA KEV
const kevCves = $('CISA_KEV').first()?.json?.vulnerabilities || [];
const kevSet = new Set(kevCves.map(v => v.cveID));

const prioritized = critical.map(v => ({
  ...v,
  actively_exploited: kevSet.has(v.cve_id),
  priority: kevSet.has(v.cve_id) ? "P0_IMMEDIATE" : "P1_URGENT",
}));

// Ordenar: explotadas activamente primero
prioritized.sort((a, b) =>
  (b.actively_exploited ? 1 : 0) - (a.actively_exploited ? 1 : 0)
);

return prioritized.map(v => ({ json: v }));

Workflow 5: Creación automática de tickets de incidentes

Cuando se detecta un incidente (alerta del SIEM, resultado de hunting, reporte de phishing confirmado), este workflow crea automáticamente el ticket en el sistema de gestión con toda la información contextual.

Trigger: Webhook (desde el SIEM, otro workflow de n8n o un script)

Estructura

  1. Webhook Trigger: recibe datos del incidente
  2. Code node: clasificar severidad y tipo de incidente
  3. HTTP Request: enriquecer con contexto (IOCs, usuario afectado, sistema)
  4. Code node: generar descripción estructurada del ticket
  5. Jira/TheHive/ServiceNow node: crear el ticket
  6. Slack node: notificar al canal del SOC con enlace al ticket
  7. Respond to Webhook: devolver el ID del ticket creado
// Generar descripcion estructurada del ticket
const incident = $input.first().json;
const enrichment = $('Enrichment').first()?.json || {};

const severityMap = {
  critical: { jira_priority: "Highest", sla_hours: 1 },
  high: { jira_priority: "High", sla_hours: 4 },
  medium: { jira_priority: "Medium", sla_hours: 24 },
  low: { jira_priority: "Low", sla_hours: 72 },
};

const severity = incident.severity || "medium";
const config = severityMap[severity];

const description = `
h2. Incident Details

||Field||Value||
|Type|${incident.type || "Unknown"}|
|Source|${incident.source || "SIEM"}|
|Severity|${severity.toUpperCase()}|
|SLA|${config.sla_hours} hours|
|Detected|${new Date().toISOString()}|

h2. Affected Assets

${incident.affected_hosts?.map(h =>
  `* ${h}`
).join("\n") || "* Not determined"}

h2. Indicators of Compromise

${incident.iocs?.map(ioc =>
  `* {{${ioc.type}}}: {{${ioc.value}}}`
).join("\n") || "* None identified yet"}

h2. Enrichment

${enrichment.summary || "Pending enrichment"}

h2. Recommended Actions

${incident.recommended_actions?.map((a, i) =>
  `# ${a}`
).join("\n") || "# Investigate and contain"}
`;

return [{
  json: {
    summary: `[${severity.toUpperCase()}] ${incident.type}: ${incident.title}`,
    description,
    priority: config.jira_priority,
    labels: ["security-incident", incident.type, severity],
    sla_deadline: new Date(
      Date.now() + config.sla_hours * 3600000
    ).toISOString(),
  }
}];

Buenas prácticas de despliegue

Seguridad de la instancia n8n

  • Autenticación: activa siempre la autenticación (N8N_BASIC_AUTH_ACTIVE o N8N_USER_MANAGEMENT_DISABLED=false). Una instancia n8n sin autenticación es un riesgo crítico.
  • HTTPS: despliega detrás de un reverse proxy (Caddy, Nginx) con TLS. Los webhooks reciben datos sensibles.
  • Red interna: si es posible, no expongas n8n a Internet. Usa VPN o Tailscale para acceso remoto. Los webhooks pueden pasar por un reverse proxy con IP whitelisting.
  • Credenciales: usa las credenciales nativas de n8n (cifradas en la base de datos). No pongas API keys en los nodos Code.
  • Backups: el volumen de n8n contiene workflows, credenciales y configuración. Haz backup diario. Un disco lleno o una corrupción de base de datos puede dejarte sin automatización.

Monitorización

Monitoriza la instancia de n8n como cualquier servicio crítico:

  • Health check: n8n expone /healthz (o configúralo con N8N_METRICS=true para Prometheus)
  • Ejecuciones fallidas: configura alertas para workflows que fallan repetidamente
  • Espacio en disco: n8n almacena logs de ejecución que crecen con el tiempo. Configura retención (N8N_EXECUTIONS_DATA_PRUNE=true)
  • Latencia de webhooks: si un webhook tarda más de 30 segundos en responder, la fuente puede cancelar la conexión

Escalabilidad

Para entornos con muchos workflows concurrentes:

  • Modo queue: n8n soporta separar el proceso principal (que gestiona triggers y UI) de los workers (que ejecutan workflows). Esto permite escalar horizontalmente.
  • Base de datos externa: en lugar de SQLite (por defecto), usa PostgreSQL para mejor rendimiento y fiabilidad.
  • Workers dedicados: los workflows de seguridad que consultan APIs externas (VirusTotal, etc.) pueden saturar la instancia si se ejecutan muchos en paralelo. Workers dedicados evitan que un workflow lento bloquee a otros.

Recursos

  • n8n Documentation: documentación oficial de n8n con guías de instalación, nodos disponibles y ejemplos de workflows. Disponible en docs.n8n.io.
  • n8n Community Workflows: biblioteca de workflows creados por la comunidad. Incluye ejemplos de seguridad y DevOps. Disponible en n8n.io/workflows.
  • Shuffle SOAR: plataforma SOAR open source con enfoque en seguridad. Más especializada que n8n para operaciones SOC, pero con menor comunidad. GitHub: shuffle/shuffle.
  • TheHive API: documentación de la API de TheHive para integración con n8n via HTTP Request nodes. Disponible en docs.thehive-project.org.
  • VirusTotal API v3: documentación de la API de VirusTotal. La versión gratuita permite 4 consultas por minuto, suficiente para la mayoría de workflows. developers.virustotal.com.
  • AbuseIPDB API: API para consultar reputación de IPs. Gratuita hasta 1.000 consultas diarias. Documentación en docs.abuseipdb.com.

Preguntas frecuentes

Artículos relacionados

Este contenido tiene fines exclusivamente educativos y de investigación en ciberseguridad defensiva. No se proporcionan binarios maliciosos ni payloads ejecutables. El uso indebido de esta información es responsabilidad exclusiva del usuario. Leer disclaimer completo.