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.
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
| Aspecto | n8n | SOAR enterprise |
|---|---|---|
| Coste | Gratis (self-hosted) o desde 20 EUR/mes | Desde 50.000 EUR/año |
| Despliegue | Docker en cualquier VPS (30 minutos) | Semanas o meses |
| Integraciones security | Via HTTP Request + Code nodes | Nativas (cientos de conectores) |
| Gestión de casos | No nativa (integra con TheHive, Jira) | Incluida |
| Aprobación humana (HITL) | Via webhook + Slack/email | Nativa con UI dedicada |
| Dashboards SOC | No nativo (integra con Grafana) | Incluidos |
| Curva de aprendizaje | Baja (interfaz visual) | Alta (configuración compleja) |
| Escalabilidad | Buena (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
- Webhook Trigger: recibe el IOC
- Switch node: determina el tipo de IOC
- HTTP Request nodes (en paralelo):
- VirusTotal API
- AbuseIPDB (para IPs)
- URLhaus (para dominios/URLs)
- MalwareBazaar (para hashes)
- Shodan (para IPs)
- Code node: consolidar resultados y calcular score
- IF node: si score es mayor de 70, crear alerta
- 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
- IMAP Trigger: detecta nuevo email en el buzón de phishing
- Code node: extraer URLs del body del email
- Split In Batches: procesar cada URL
- HTTP Request: consultar URLhaus y VirusTotal por cada URL
- Code node: extraer adjuntos y calcular hashes SHA256
- HTTP Request: consultar MalwareBazaar por cada hash
- Code node: calcular score total y generar veredicto
- 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
- Cron Trigger: 07:00 L-V
- 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)
- 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}` } }];
- 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
- Webhook/Cron Trigger: recibir o consultar resultados
- Code node: filtrar vulnerabilidades por severidad
- IF node: separar críticas y altas del resto
- HTTP Request: buscar en CISA KEV si la CVE está activamente explotada
- Code node: priorizar por explotación activa + CVSS
- Slack node: notificar vulnerabilidades críticas al canal de seguridad
- Jira/ServiceNow node: crear tickets para remediación
- 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
- Webhook Trigger: recibe datos del incidente
- Code node: clasificar severidad y tipo de incidente
- HTTP Request: enriquecer con contexto (IOCs, usuario afectado, sistema)
- Code node: generar descripción estructurada del ticket
- Jira/TheHive/ServiceNow node: crear el ticket
- Slack node: notificar al canal del SOC con enlace al ticket
- 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
Libros recomendados
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.