Registro y monitorización de seguridad
Los registros son la caja negra de su infraestructura. Cuando algo sale mal — una brecha, una interrupción, un comportamiento inexplicable — los registros son la forma de entender qué ocurrió. Sin un registro adecuado, investiga los incidentes a ciegas.
La monitorización de seguridad va más allá del registro: en lugar de esperar a que alguien note un problema y compruebe los registros, está vigilando activamente los patrones sospechosos y alertando en tiempo real. Un ataque de fuerza bruta que genera miles de inicios de sesión fallidos. Una cuenta de administrador que de repente accede a datos que nunca tocó antes. Un servidor que hace conexiones a IPs maliciosas conocidas.
Este capítulo cubre cómo construir una infraestructura de registro y monitorización que detecte incidentes de seguridad cuando ocurren, no semanas después cuando el daño ya está hecho.
Por qué esto importa para las empresas pequeñas
Las empresas pequeñas a menudo se saltan el registro adecuado. «Revisaremos los registros si algo se rompe.» Ese enfoque falla en seguridad:
No puede investigar lo que no registró. Cuando se descubre una brecha, la primera pregunta es «¿qué ocurrió?». Sin registros, no puede responderla. No sabe a qué datos se accedió, cómo entraron los atacantes, ni si siguen ahí.
Los atacantes borran sus rastros. Si los registros solo existen en el servidor comprometido, los atacantes los eliminan. El registro centralizado en un sistema separado preserva la evidencia incluso cuando los servidores están comprometidos.
La detección requiere visibilidad. La mayoría de las brechas pasan desapercibidas durante meses. El informe de IBM 2024 sobre el coste de una brecha de datos encontró que el tiempo medio para identificar una brecha es de 194 días. Las empresas con monitorización de seguridad detectan las brechas más rápido y pierden menos dinero.
El cumplimiento normativo a menudo lo requiere. SOC 2, ISO 27001, HIPAA, PCI-DSS, todos exigen registro y monitorización de seguridad. Aunque no lo necesite ahora, puede necesitarlo más adelante.
Las herramientas gratuitas son maduras. ELK Stack, Grafana Loki, Graylog — estos no son juguetes. Gestionan registros a escala empresarial. No necesita un SIEM de 100.000 dólares para tener un registro adecuado.
Qué registrar
No todos los registros son iguales para la seguridad. Esto es lo que importa:
Autenticación y acceso
| Evento | Por qué importa | Prioridad |
|---|---|---|
| Éxito/fallo de inicio de sesión | Detección de fuerza bruta, acceso no autorizado | Crítica |
| Cambios de contraseña | Indicador de toma de control de cuenta | Crítica |
| Eventos MFA | Intentos de bypass, cambios de inscripción | Crítica |
| Creación/destrucción de sesión | Secuestro de sesión, patrones de acceso anómalos | Alta |
| Cambios de permisos | Escalada de privilegios, amenazas internas | Crítica |
| Uso de claves de API | Credenciales comprometidas | Alta |
| Conexiones SSH/RDP | Movimiento lateral, acceso inicial | Crítica |
Seguridad de aplicaciones
| Evento | Por qué importa | Prioridad |
|---|---|---|
| Fallos de validación de entrada | Intentos de ataque (SQLi, XSS) | Alta |
| Denegaciones de autorización | Intentos de control de acceso roto | Alta |
| Operaciones de archivo | Exfiltración de datos, ransomware | Media |
| Eventos de pago/transacción | Detección de fraude | Crítica |
| Tasas de error | Pueden indicar ataques | Media |
| Límites de velocidad de API alcanzados | Abuso, scraping, DDoS | Media |
Infraestructura
| Evento | Por qué importa | Prioridad |
|---|---|---|
| Permitir/denegar en firewall | Reconocimiento de red, ataques | Alta |
| Consultas DNS | Comunicación C2, exfiltración de datos | Media |
| Ejecución de procesos | Malware, cryptomining | Alta |
| Inicio/parada de servicios | Manipulación, persistencia | Media |
| Cambios de integridad de archivos | Rootkits, backdoors | Alta |
| Llamadas a la API cloud | Abuso de recursos, configuración incorrecta | Alta |
Qué NO registrar
Algunos datos nunca deben aparecer en los registros:
- Contraseñas (ni siquiera las fallidas — registre «autenticación fallida», no «la contraseña 'abc123' era incorrecta»)
- Números de tarjeta de crédito completos (registre solo los últimos 4 dígitos)
- Números de seguridad social, documentos de identidad gubernamentales
- Información de salud (PHI)
- Tokens de sesión, claves de API (registre que se usaron, no sus valores)
- Datos personales más allá de lo necesario
Registrar datos sensibles crea un nuevo riesgo de brecha: su almacenamiento de registros se convierte en un objetivo valioso.
Formatos y estándares de registro
Los formatos de registro consistentes facilitan el análisis y la creación de alertas.
Registro estructurado
Use formatos estructurados (JSON) en lugar de texto plano:
# BAD: Unstructured log
logger.info(f"User {user_id} logged in from {ip_address}")
# Output: 2024-01-15 10:23:45 INFO User 12345 logged in from 192.168.1.1
# Hard to parse, inconsistent format
# GOOD: Structured log
import structlog
logger = structlog.get_logger()
logger.info(
"user_login",
user_id=user_id,
ip_address=ip_address,
user_agent=request.headers.get("User-Agent"),
success=True
)
# Output (JSON):
# {"event": "user_login", "user_id": 12345, "ip_address": "192.168.1.1",
# "user_agent": "Mozilla/5.0...", "success": true, "timestamp": "2024-01-15T10:23:45Z"}
El registro estructurado permite:
- Análisis consistente en todas las aplicaciones
- Filtrado fácil (ej., «mostrar todos los inicios de sesión fallidos desde esta IP»)
- Extracción automática de campos en herramientas SIEM
- Mejor compresión y eficiencia de almacenamiento
Campos comunes de registro
Cada entrada de registro debe incluir:
{
"timestamp": "2024-01-15T10:23:45.123Z",
"level": "info",
"event": "user_login",
"service": "auth-api",
"environment": "production",
"host": "auth-api-1.internal",
"trace_id": "abc123def456",
"user_id": "12345",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"success": true,
"duration_ms": 45
}
| Campo | Propósito |
|---|---|
timestamp | Cuándo ocurrió (ISO 8601, UTC) |
level | Gravedad (debug, info, warn, error, critical) |
event | Qué ocurrió (use nombres de evento consistentes) |
service | Qué aplicación/servicio |
environment | prod, staging, dev |
host | Qué servidor/contenedor |
trace_id | Correlacionar eventos relacionados |
user_id | Quién desencadenó el evento |
ip_address | De dónde vino la solicitud |
Niveles de registro
Use los niveles de registro de forma consistente:
| Nivel | Cuándo usarlo | Ejemplo |
|---|---|---|
| DEBUG | Información de diagnóstico detallada, solo en dev | Valores de variables, llamadas a funciones |
| INFO | Operaciones normales | Usuario inició sesión, solicitud completada |
| WARN | Algo inesperado pero gestionado | Límite de velocidad aproximándose, reintento exitoso |
| ERROR | Algo falló | Conexión de base de datos fallida, error de API |
| CRITICAL | Fallo a nivel de sistema | Servicio caído, corrupción de datos |
Los eventos de seguridad deben ser generalmente INFO (eventos de seguridad normales) o WARN/ERROR (intentos sospechosos/fallidos).
Arquitectura de registro centralizado
Los registros dispersos en decenas de servidores son inútiles durante un incidente. Necesita recopilación centralizada.
Arquitectura básica
┌─────────────────────────────────────────────────────────────────────┐
│ Log Sources │
├─────────────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Web App │ │ API │ │ Database│ │ Firewall│ │ Cloud │ │
│ │ Logs │ │ Logs │ │ Logs │ │ Logs │ │ Logs │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼────────────┼────────────┼────────────┼────────────┼─────────┘
│ │ │ │ │
└────────────┴────────────┴─────┬──────┴────────────┘
│
▼
┌─────────────────────────┐
│ Log Collector │
│ (Fluentd, Filebeat, │
│ Vector, Fluent Bit) │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Log Aggregator │
│ (Elasticsearch, Loki, │
│ CloudWatch, Graylog) │
└───────────┬─────────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ Visualization │ │ Alerting │
│ (Kibana, Grafana)│ │ (AlertManager, │
│ │ │ PagerDuty) │
└───────────────────┘ └───────────────────┘
Componentes explicados
Fuentes de registro: Todo lo que genera registros — aplicaciones, servidores, dispositivos de red, servicios cloud.
Recopiladores de registros: Agentes que se ejecutan en cada servidor, recopilan registros y los reenvían. Gestionan:
- Seguimiento de archivos de registro
- Análisis y enriquecimiento
- Almacenamiento en búfer (si la red está caída)
- Compresión
Agregador de registros: Almacenamiento central que recibe, indexa y almacena registros. Proporciona:
- Capacidades de búsqueda
- Retención a largo plazo
- Agregación y análisis
Visualización: Paneles de control para explorar registros, construir consultas e investigar incidentes.
Alertas: Reglas que desencadenan notificaciones cuando aparecen patrones sospechosos.
Pilas de registro gratuitas
Opción 1: ELK Stack (Elasticsearch + Logstash + Kibana)
La pila clásica de registro de código abierto. Los datos fluyen en una dirección:
Servidores (emiten registros) → Filebeat (recopila y envía) → Logstash (analiza y procesa) → Elasticsearch (almacena e indexa) → Kibana (visualiza y busca)
Configuración con Docker Compose:
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=true
- ELASTIC_PASSWORD=changeme
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
healthcheck:
test: curl -s http://localhost:9200 >/dev/null || exit 1
interval: 30s
timeout: 10s
retries: 5
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=changeme
ports:
- "5601:5601"
depends_on:
elasticsearch:
condition: service_healthy
logstash:
image: docker.elastic.co/logstash/logstash:8.12.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
ports:
- "5044:5044" # Beats input
- "5000:5000" # TCP input
depends_on:
elasticsearch:
condition: service_healthy
volumes:
elasticsearch-data:
Configuración del pipeline de Logstash:
# logstash/pipeline/main.conf
input {
beats {
port => 5044
}
tcp {
port => 5000
codec => json_lines
}
}
filter {
# Parse JSON logs
if [message] =~ /^\{/ {
json {
source => "message"
}
}
# Parse common log formats
if [type] == "nginx" {
grok {
match => { "message" => '%{COMBINEDAPACHELOG}' }
}
}
# GeoIP enrichment for IP addresses
if [ip_address] {
geoip {
source => "ip_address"
target => "geoip"
}
}
# Add security-relevant tags
if [event] == "login_failed" {
mutate {
add_tag => ["security", "authentication"]
}
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
user => "elastic"
password => "changeme"
index => "logs-%{+YYYY.MM.dd}"
}
}
Configuración de Filebeat (en cada servidor):
# /etc/filebeat/filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
- /var/log/nginx/error.log
fields:
type: nginx
- type: log
enabled: true
paths:
- /var/log/auth.log
fields:
type: auth
- type: log
enabled: true
paths:
- /app/logs/*.json
json.keys_under_root: true
json.add_error_key: true
fields:
type: application
output.logstash:
hosts: ["logstash.internal:5044"]
# Or direct to Elasticsearch
# output.elasticsearch:
# hosts: ["elasticsearch.internal:9200"]
# username: "elastic"
# password: "changeme"
Ventajas:
- Búsqueda y análisis extremadamente potentes
- Gran ecosistema y comunidad
- Escala hasta petabytes
- Gratuito y de código abierto (licencia básica)
Desventajas:
- Consume muchos recursos (necesita RAM considerable para Elasticsearch)
- Complejo de operar a escala
- Algunas funciones requieren licencia de pago
Opción 2: Grafana Loki + Promtail
Una alternativa más nueva y ligera. Mismo flujo lineal, menos componentes:
Servidores (emiten registros) → Promtail (recopila y envía) → Loki (almacena) → Grafana (visualiza)
Configuración con Docker Compose:
version: '3.8'
services:
loki:
image: grafana/loki:2.9.3
ports:
- "3100:3100"
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml
grafana:
image: grafana/grafana:10.3.1
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=changeme
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- loki
promtail:
image: grafana/promtail:2.9.3
volumes:
- ./promtail-config.yaml:/etc/promtail/config.yml
- /var/log:/var/log:ro
command: -config.file=/etc/promtail/config.yml
volumes:
loki-data:
grafana-data:
Configuración de Loki:
# loki-config.yaml
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://alertmanager:9093
Configuración de Promtail:
# promtail-config.yaml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*log
- job_name: nginx
static_configs:
- targets:
- localhost
labels:
job: nginx
__path__: /var/log/nginx/*.log
pipeline_stages:
- regex:
expression: '^(?P<remote_addr>[\d\.]+) - (?P<remote_user>\S+) \[(?P<time_local>.+)\] "(?P<method>\S+) (?P<request>\S+) (?P<protocol>\S+)" (?P<status>\d+)'
- labels:
method:
status:
- job_name: application
static_configs:
- targets:
- localhost
labels:
job: app
__path__: /app/logs/*.json
pipeline_stages:
- json:
expressions:
level: level
event: event
user_id: user_id
- labels:
level:
event:
Ventajas:
- Mucho menos consumo de recursos que Elasticsearch
- Integración nativa con Grafana
- La indexación basada en etiquetas es muy eficiente
- Fácil de configurar y operar
Desventajas:
- Búsqueda de texto completo menos potente que Elasticsearch
- Ecosistema más pequeño
- Más reciente, menos probado en batalla
Opción 3: Graylog
Una solución completa de gestión de registros con funciones similares a SIEM integradas:
version: '3.8'
services:
mongodb:
image: mongo:6
volumes:
- mongo-data:/data/db
opensearch:
image: opensearchproject/opensearch:2.11.0
environment:
- discovery.type=single-node
- plugins.security.disabled=true
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
volumes:
- opensearch-data:/usr/share/opensearch/data
graylog:
image: graylog/graylog:5.2
environment:
- GRAYLOG_PASSWORD_SECRET=somesecretpasswordpepper
- GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
- GRAYLOG_HTTP_EXTERNAL_URI=http://localhost:9000/
- GRAYLOG_MONGODB_URI=mongodb://mongodb:27017/graylog
- GRAYLOG_ELASTICSEARCH_HOSTS=http://opensearch:9200
ports:
- "9000:9000" # Web interface
- "1514:1514" # Syslog TCP
- "1514:1514/udp" # Syslog UDP
- "12201:12201" # GELF TCP
- "12201:12201/udp" # GELF UDP
depends_on:
- mongodb
- opensearch
volumes:
mongo-data:
opensearch-data:
Ventajas:
- Alertas integradas y funciones similares a SIEM
- Interfaz amigable para el usuario
- Bueno para casos de uso de seguridad desde el principio
- Soporta GELF (formato de registro estructurado)
Desventajas:
- Arquitectura más compleja (requiere MongoDB + OpenSearch)
- Menos flexible que ELK raw
- Algunas funciones avanzadas requieren licencia empresarial
Opciones nativas de la nube
Si está ejecutando en la nube, los servicios de registro nativos suelen ser la opción más sencilla:
AWS CloudWatch Logs:
# Install CloudWatch agent
sudo yum install amazon-cloudwatch-agent
# Configure (wizard available)
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
Archivo de configuración:
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/nginx/access.log",
"log_group_name": "nginx-access",
"log_stream_name": "{instance_id}"
},
{
"file_path": "/app/logs/*.json",
"log_group_name": "application",
"log_stream_name": "{instance_id}",
"timestamp_format": "%Y-%m-%dT%H:%M:%S"
}
]
}
}
}
}
GCP Cloud Logging:
Automático para instancias GCE. Para aplicaciones, use la biblioteca:
from google.cloud import logging
client = logging.Client()
logger = client.logger("my-application")
logger.log_struct({
"event": "user_login",
"user_id": "12345",
"success": True
}, severity="INFO")
Azure Monitor Logs:
Use el workspace de Log Analytics y el agente de Azure Monitor.
Tabla de comparación
| Característica | ELK Stack | Grafana Loki | Graylog | CloudWatch | GCP Logging |
|---|---|---|---|---|---|
| Coste | Gratuito (OSS) | Gratuito (OSS) | Gratuito (OSS) | Pago por GB | Pago por GB |
| Complejidad de configuración | Alta | Media | Media | Baja | Baja |
| Uso de recursos | Alto | Bajo | Medio | N/A | N/A |
| Búsqueda de texto completo | Excelente | Buena | Excelente | Buena | Buena |
| Alertas | Requiere configuración | Integradas | Integradas | Integradas | Integradas |
| Retención | Ilimitada | Ilimitada | Ilimitada | Configurable | Configurable |
| Escalabilidad | Excelente | Excelente | Buena | Excelente | Excelente |
| Mejor para | Gran escala, consultas complejas | Kubernetes, usuarios de Grafana | Enfoque en seguridad | Entornos AWS | Entornos GCP |
Configuración de alertas
Los registros solo son útiles si los está vigilando. Las alertas convierten los registros pasivos en monitorización activa.
Categorías de alertas
Crítico — respuesta inmediata requerida:
- Múltiples inicios de sesión fallidos seguidos de éxito (credential stuffing exitoso)
- Usuario administrador creado por un no-administrador
- Firewall deshabilitado
- Ejecución de proceso sospechoso (firmas de cryptominer)
- Conexión a IP maliciosa conocida
- Base de datos de producción accedida desde IP desconocida
Alto — investigar en horas:
- Ataques de fuerza bruta en curso
- Patrones de uso de API inusuales
- Acceso de administrador fuera de horario
- Exportaciones de datos masivas
- Eventos de escalada de permisos
Medio — revisar diariamente:
- Umbral de inicios de sesión fallidos superado
- Picos en la tasa de errores
- Acceso geográfico inusual
- Advertencias de expiración de certificado
Bajo — revisión semanal:
- Acceso desde nuevos dispositivos
- Intentos de violación de políticas
- Cambios de configuración menores
Ejemplos de reglas de alerta
Alertas en ELK/Kibana:
{
"trigger": {
"schedule": {
"interval": "1m"
}
},
"input": {
"search": {
"request": {
"indices": ["logs-*"],
"body": {
"query": {
"bool": {
"must": [
{ "match": { "event": "login_failed" } },
{ "range": { "@timestamp": { "gte": "now-5m" } } }
]
}
},
"aggs": {
"by_ip": {
"terms": { "field": "ip_address", "size": 10 }
}
}
}
}
}
},
"condition": {
"script": {
"source": "return ctx.payload.aggregations.by_ip.buckets.stream().anyMatch(b -> b.doc_count > 10)"
}
},
"actions": {
"notify_slack": {
"slack": {
"message": {
"to": ["#security-alerts"],
"text": "Brute force attack detected: {{ctx.payload.aggregations.by_ip.buckets}}"
}
}
}
}
}
Alertas de Grafana Loki (via Grafana):
# grafana/provisioning/alerting/rules.yaml
apiVersion: 1
groups:
- orgId: 1
name: security-alerts
folder: Security
interval: 1m
rules:
- uid: brute-force-detection
title: Brute Force Attack Detected
condition: C
data:
- refId: A
datasourceUid: loki
model:
expr: 'sum(rate({job="app"} |= "login_failed" [5m])) by (ip_address)'
- refId: B
datasourceUid: __expr__
model:
expression: A
type: reduce
reducer: last
- refId: C
datasourceUid: __expr__
model:
expression: B > 10
type: threshold
for: 5m
annotations:
summary: "Brute force attack from {{ $labels.ip_address }}"
labels:
severity: critical
Alarmas de AWS CloudWatch:
# Create metric filter for failed logins
aws logs put-metric-filter \
--log-group-name application \
--filter-name failed-logins \
--filter-pattern '{ $.event = "login_failed" }' \
--metric-transformations \
metricName=FailedLogins,metricNamespace=Security,metricValue=1
# Create alarm
aws cloudwatch put-metric-alarm \
--alarm-name BruteForceDetection \
--metric-name FailedLogins \
--namespace Security \
--statistic Sum \
--period 300 \
--threshold 50 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789:security-alerts
Reglas de detección de seguridad
Patrones comunes sobre los que alertar:
Ataques de autenticación
# Failed login spike (brute force)
- name: brute_force_attack
query: |
event:login_failed | stats count() by ip_address | where count > 20
window: 5m
severity: high
# Successful login after many failures (compromise)
- name: credential_stuffing_success
query: |
(event:login_failed | stats count() as failures by user_id, ip_address)
| join (event:login_success by user_id, ip_address)
| where failures > 5
window: 15m
severity: critical
# Login from new country
- name: impossible_travel
query: |
event:login_success
| geoip(ip_address)
| stats distinct_count(country) as countries by user_id
| where countries > 1
window: 1h
severity: high
Escalada de privilegios
# Admin user created
- name: admin_user_created
query: |
event:user_created AND role:admin
severity: high
# Privilege escalation
- name: privilege_escalation
query: |
event:role_changed AND new_role:(admin OR superuser)
severity: high
# Service account used interactively
- name: service_account_interactive
query: |
event:login_success
AND user_type:service_account
AND session_type:interactive
severity: critical
Exfiltración de datos
# Large data export
- name: large_data_export
query: |
event:data_export
| stats sum(record_count) as total by user_id
| where total > 10000
window: 1h
severity: high
# Database query spike
- name: unusual_database_queries
query: |
source:database
| stats count() as queries by user_id
| where queries > baseline_queries * 3
window: 1h
severity: medium
# After hours access to sensitive data
- name: after_hours_sensitive_access
query: |
event:data_access
AND data_classification:confidential
AND NOT between(hour, 8, 18)
severity: high
Ataques a la infraestructura
# SSH from unusual source
- name: ssh_from_internet
query: |
event:ssh_session_start
AND NOT source_ip:("10.*" OR "172.16.*" OR "192.168.*")
severity: high
# Cryptominer signatures
- name: cryptominer_detection
query: |
(process_name:*miner* OR cmdline:*stratum* OR cmdline:*xmrig*)
severity: critical
# Firewall rule change
- name: firewall_modified
query: |
event:(security_group_modified OR firewall_rule_changed)
severity: high
# Root/admin shell spawned
- name: root_shell_spawned
query: |
event:process_start AND user:root AND process:(bash OR sh OR zsh)
severity: medium
Reducción de la fatiga de alertas
Demasiadas alertas = alertas ignoradas. Así se mantienen las alertas significativas:
Establezca umbrales apropiados:
# BAD: Alerts on every failed login
- condition: event == "login_failed"
# GOOD: Alerts on suspicious patterns
- condition: count(event == "login_failed") > 10 in 5m grouped by ip_address
Añada contexto:
# BAD: Just says "suspicious activity"
message: "Suspicious activity detected"
# GOOD: Actionable information
message: |
Brute force attack detected
Source IP: {{ ip_address }}
Target users: {{ affected_users }}
Failed attempts: {{ count }}
Timeframe: last 5 minutes
Action: Consider blocking IP in WAF
Use los niveles de gravedad correctamente:
- CRITICAL: Alguien tiene que despertar
- HIGH: Investigar en horas
- MEDIUM: Comprobar durante el horario laboral
- LOW: Revisión semanal
Ajuste según los comentarios:
- Rastree la tasa de falsos positivos
- Si > 50% son falsos positivos, ajuste el umbral
- Si hay 0% de alertas, puede que esté perdiendo cosas
Enrutamiento de alertas
Envíe las alertas al lugar correcto:
# alertmanager.yml
route:
receiver: default
routes:
# Critical security alerts → PagerDuty
- match:
severity: critical
category: security
receiver: pagerduty-security
# High security alerts → Slack security channel
- match:
severity: high
category: security
receiver: slack-security
# Infrastructure alerts → Slack ops channel
- match:
category: infrastructure
receiver: slack-ops
receivers:
- name: pagerduty-security
pagerduty_configs:
- service_key: YOUR_PAGERDUTY_KEY
severity: critical
- name: slack-security
slack_configs:
- api_url: https://hooks.slack.com/services/XXX
channel: '#security-alerts'
- name: slack-ops
slack_configs:
- api_url: https://hooks.slack.com/services/XXX
channel: '#ops-alerts'
Conceptos SIEM
La Gestión de Información y Eventos de Seguridad (SIEM) combina la gestión de registros con el análisis de seguridad. Aunque las plataformas SIEM completas son caras, puede construir capacidades similares a SIEM con herramientas de código abierto.
Qué añade un SIEM
| Capacidad | Gestión de registros | SIEM |
|---|---|---|
| Recopilación de registros | ✓ | ✓ |
| Búsqueda y visualización | ✓ | ✓ |
| Correlación entre fuentes | Limitada | ✓ |
| Integración de inteligencia de amenazas | Manual | Automática |
| Análisis de comportamiento | No | ✓ |
| Informes de cumplimiento | Manual | Integrado |
| Gestión de casos | No | ✓ |
| Respuesta automatizada | Limitada | ✓ |
Construcción de capacidades similares a SIEM
1. Correlación de registros:
Combine registros de múltiples fuentes para detectar ataques:
# Detect: SSH brute force followed by successful login, then suspicious command
correlation_rule:
name: ssh_compromise_chain
events:
- event_type: ssh_failed_login
count: ">10"
group_by: source_ip, target_host
window: 5m
save_as: brute_force
- event_type: ssh_successful_login
source_ip: $brute_force.source_ip
target_host: $brute_force.target_host
within: 10m
after: brute_force
save_as: compromise
- event_type: command_execution
host: $compromise.target_host
command: ("wget*" OR "curl*" OR "nc*")
within: 30m
after: compromise
alert:
severity: critical
message: "Likely SSH compromise: brute force from {source_ip} → successful login → suspicious commands"
2. Integración de inteligencia de amenazas:
Cruce de IPs y dominios con actores maliciosos conocidos:
# Example: Check IPs against threat intel feed
import requests
def check_ip_reputation(ip_address):
# Check against AbuseIPDB
response = requests.get(
"https://api.abuseipdb.com/api/v2/check",
headers={"Key": ABUSEIPDB_API_KEY},
params={"ipAddress": ip_address}
)
data = response.json()["data"]
if data["abuseConfidenceScore"] > 80:
return {
"malicious": True,
"score": data["abuseConfidenceScore"],
"reports": data["totalReports"],
"categories": data["usageType"]
}
return {"malicious": False}
3. Análisis del comportamiento de usuarios y entidades (UEBA):
Detecte anomalías basándose en el comportamiento normal:
# Baseline: User typically logs in 9am-6pm from US
# Alert: Same user logging in at 3am from Russia
def detect_anomalous_login(user_id, login_event):
# Get user's normal login patterns
baseline = get_user_baseline(user_id)
# Check for anomalies
anomalies = []
# Time anomaly
if not baseline.normal_hours.contains(login_event.hour):
anomalies.append({
"type": "unusual_time",
"expected": baseline.normal_hours,
"actual": login_event.hour
})
# Location anomaly
if login_event.country not in baseline.normal_countries:
anomalies.append({
"type": "new_location",
"expected": baseline.normal_countries,
"actual": login_event.country
})
# Impossible travel
if baseline.last_login:
distance = calculate_distance(
baseline.last_login.location,
login_event.location
)
time_diff = login_event.time - baseline.last_login.time
if distance / time_diff.hours > 500: # 500 km/h impossible
anomalies.append({
"type": "impossible_travel",
"distance_km": distance,
"time_hours": time_diff.hours
})
return anomalies
Alternativas SIEM gratuitas
| Herramienta | Tipo | Mejor para | Enlace |
|---|---|---|---|
| Wazuh | SIEM completo | Monitorización de seguridad integral | wazuh.com |
| Security Onion | Monitorización de seguridad de red | Seguridad centrada en la red | securityonion.net |
| OSSEC | IDS basado en host | Integridad de archivos, detección de rootkits | ossec.net |
| Suricata | IDS de red | Detección de amenazas en la red | suricata.io |
| TheHive | Respuesta a incidentes | Gestión de casos, playbooks | thehive-project.org |
Configuración de Wazuh (Docker):
# docker-compose.yml for Wazuh
version: '3.8'
services:
wazuh.manager:
image: wazuh/wazuh-manager:4.7.2
hostname: wazuh.manager
ports:
- "1514:1514"
- "1515:1515"
- "514:514/udp"
- "55000:55000"
environment:
- INDEXER_URL=https://wazuh.indexer:9200
- FILEBEAT_SSL_VERIFICATION_MODE=none
volumes:
- wazuh_api_configuration:/var/ossec/api/configuration
- wazuh_etc:/var/ossec/etc
- wazuh_logs:/var/ossec/logs
wazuh.indexer:
image: wazuh/wazuh-indexer:4.7.2
hostname: wazuh.indexer
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
volumes:
- wazuh-indexer-data:/var/lib/wazuh-indexer
wazuh.dashboard:
image: wazuh/wazuh-dashboard:4.7.2
hostname: wazuh.dashboard
ports:
- "443:5601"
environment:
- INDEXER_USERNAME=admin
- INDEXER_PASSWORD=SecretPassword
- WAZUH_API_URL=https://wazuh.manager
depends_on:
- wazuh.indexer
- wazuh.manager
volumes:
wazuh_api_configuration:
wazuh_etc:
wazuh_logs:
wazuh-indexer-data:
Wazuh proporciona:
- Monitorización de integridad de archivos
- Detección de rootkits
- Análisis de registros
- Paneles de cumplimiento (PCI-DSS, GDPR, HIPAA)
- Respuesta activa (bloqueo automático)
- Detección de vulnerabilidades
Retención de registros y cumplimiento
Requisitos de retención
| Estándar | Retención mínima | Notas |
|---|---|---|
| PCI-DSS | 1 año | 3 meses disponibles inmediatamente |
| HIPAA | 6 años | Puede variar por estado |
| SOC 2 | 1 año | Depende de los criterios de confianza |
| GDPR | Varía | «No más tiempo del necesario» |
| ISO 27001 | 3 años recomendado | El tiempo necesario para la seguridad |
| Mejor práctica interna | 90 días caliente, 1 año frío | Equilibre coste y utilidad |
Niveles de almacenamiento
| Nivel | Retención | Velocidad de consulta | Coste | Indexación | Ejemplo |
|---|---|---|---|---|---|
| Caliente | 7–30 días | Rápida | Alto | Completa | Elasticsearch en SSD |
| Templado | 30–90 días | Más lenta | Moderado | Reducida | Elasticsearch en HDD, S3 + Athena |
| Frío | 90+ días | Solo archivo | Bajo | Ninguna | S3 Glacier, archivos comprimidos |
Gestión del ciclo de vida de índices de Elasticsearch:
PUT _ilm/policy/security-logs-policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_primary_shard_size": "50gb",
"max_age": "7d"
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 },
"allocate": { "require": { "data": "warm" } }
}
},
"cold": {
"min_age": "30d",
"actions": {
"allocate": { "require": { "data": "cold" } }
}
},
"delete": {
"min_age": "365d",
"actions": { "delete": {} }
}
}
}
}
Integridad de los registros
Para el cumplimiento normativo y el análisis forense, los registros deben ser a prueba de manipulación:
Firma de registros:
import hashlib
import hmac
def sign_log_entry(entry, secret_key):
"""Sign a log entry to detect tampering"""
entry_bytes = json.dumps(entry, sort_keys=True).encode()
signature = hmac.new(
secret_key.encode(),
entry_bytes,
hashlib.sha256
).hexdigest()
entry["_signature"] = signature
return entry
def verify_log_entry(entry, secret_key):
"""Verify a log entry hasn't been tampered with"""
signature = entry.pop("_signature", None)
if not signature:
return False
entry_bytes = json.dumps(entry, sort_keys=True).encode()
expected = hmac.new(
secret_key.encode(),
entry_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
Integridad de registros de AWS CloudWatch:
# Enable log file validation for CloudTrail
aws cloudtrail update-trail \
--name main-trail \
--enable-log-file-validation
# Validate log file integrity
aws cloudtrail validate-logs \
--trail-arn arn:aws:cloudtrail:us-east-1:123456789:trail/main-trail \
--start-time 2024-01-01T00:00:00Z
Investigación de incidentes con registros
Cuando ocurre un incidente, los registros son su principal herramienta de investigación.
Flujo de investigación
┌──────────────────┐
│ Alert Received │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Initial Triage │ ¿Qué desencadenó? ¿Cuándo? ¿Qué sistemas?
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Scope Assessment│ ¿Qué alcance? ¿Qué datos? ¿Qué acceso?
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Timeline Build │ ¿Qué ocurrió primero? ¿Qué vino después?
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Root Cause │ ¿Cómo entraron? ¿Cuál era la vulnerabilidad?
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Impact Analysis │ ¿A qué se accedió? ¿Qué se modificó?
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Containment │ Detener el sangrado, preservar la evidencia
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Documentation │ Cronología, hallazgos, lecciones aprendidas
└──────────────────┘
Consultas de investigación
Encontrar el acceso inicial:
# Kibana Query Language (KQL)
# All authentication events for compromised user around incident time
user_id: "compromised_user" AND event: (login* OR auth*)
AND @timestamp >= "2024-01-15T00:00:00" AND @timestamp <= "2024-01-15T23:59:59"
|| sort @timestamp
# Loki LogQL
{job="app"} | json | user_id="compromised_user" | line_format "{{.timestamp}} {{.event}} {{.ip_address}}"
Rastrear el movimiento del atacante:
# All events from attacker's IP
ip_address: "1.2.3.4" | sort @timestamp
# All events from compromised session
session_id: "abc123" | sort @timestamp
# Commands executed on compromised host
host: "compromised-server" AND event: "command_execution" | sort @timestamp
Identificar el acceso a datos:
# Data accessed by compromised account
user_id: "compromised_user" AND event: (data_access OR data_export OR file_download)
# Unusual queries against database
source: database AND query_type: SELECT AND tables: (users OR payments OR credentials)
Construir una cronología:
def build_incident_timeline(start_time, end_time, indicators):
"""
Build timeline from logs using IOCs
indicators = {
"ip_addresses": ["1.2.3.4"],
"user_ids": ["compromised_user"],
"session_ids": ["abc123"],
"hosts": ["compromised-server"]
}
"""
timeline = []
# Query each indicator type
for ip in indicators.get("ip_addresses", []):
events = query_logs(f"ip_address:{ip}", start_time, end_time)
timeline.extend(events)
for user_id in indicators.get("user_ids", []):
events = query_logs(f"user_id:{user_id}", start_time, end_time)
timeline.extend(events)
# Deduplicate and sort
timeline = sorted(set(timeline), key=lambda e: e.timestamp)
return timeline
Lista de verificación de investigación
## Incident Investigation Checklist
### Initial Response
- [ ] Document alert details (time, type, affected systems)
- [ ] Assign incident owner
- [ ] Open incident ticket
- [ ] Notify relevant stakeholders
### Scoping
- [ ] Identify all affected user accounts
- [ ] Identify all affected systems/hosts
- [ ] Identify all IP addresses involved
- [ ] Determine time range of activity
### Evidence Collection
- [ ] Export relevant logs (preserve originals)
- [ ] Capture network traffic if ongoing
- [ ] Take memory dumps if needed
- [ ] Screenshot any relevant dashboards
### Analysis
- [ ] Build timeline of events
- [ ] Identify initial access vector
- [ ] Map lateral movement
- [ ] Identify data accessed/exfiltrated
- [ ] Identify persistence mechanisms
### Containment
- [ ] Block malicious IPs
- [ ] Disable compromised accounts
- [ ] Isolate compromised systems
- [ ] Revoke compromised credentials
### Documentation
- [ ] Write incident summary
- [ ] Create detailed timeline
- [ ] Document root cause
- [ ] List remediation actions
- [ ] Schedule post-mortem
Errores comunes a evitar
Registrar solo errores. Las operaciones normales importan para la seguridad. Necesita los inicios de sesión exitosos para detectar cuentas comprometidas, las llamadas de API exitosas para detectar exfiltración de datos. Registre eventos de nivel INFO.
Sin marca de tiempo o zona horaria incorrecta. Los registros sin marca de tiempo son inútiles para la investigación. Use el formato ISO 8601 con zona horaria UTC en todas partes.
Registrar datos sensibles. Las contraseñas, tokens y tarjetas de crédito en los registros crean una nueva vulnerabilidad. Enmascare o excluya los campos sensibles.
Sin rotación de registros. Los discos se llenan, las aplicaciones se caen. Configure la rotación y retención desde el primer día.
Alertas sin runbooks. Una alerta se dispara a las 3am. ¿Qué debe hacer el responsable de guardia? Documente los procedimientos de respuesta para cada tipo de alerta.
Demasiadas alertas. La fatiga de alertas mata la seguridad. Si está ignorando alertas, no está monitorizando. Ajuste los umbrales y suprima el ruido.
Registros solo en los servidores de aplicación. Los eventos de seguridad ocurren en todas partes — firewalls, bases de datos, APIs cloud, balanceadores de carga. Recójalos todos.
Sin pruebas. ¿Cómo sabe que sus alertas funcionan? Genere eventos de prueba, verifique que se disparan, compruebe que se enrutan correctamente.
Incidentes reales
Brecha de Target (2013): Los atacantes estuvieron en la red durante semanas. Las herramientas de seguridad generaron alertas sobre el malware, pero fueron ignoradas debido a la fatiga de alertas. Se robaron 40 millones de tarjetas de crédito. Fuente: Comité del Senado de EE.UU.
Brecha de Equifax (2017): El ataque comenzó en mayo, se descubrió en julio. El registro deficiente significó que los investigadores no podían determinar el alcance total de la brecha. Se expusieron 147 millones de registros. La investigación tardó meses porque los datos de registro estaban incompletos. Fuente: Informe GAO
SolarWinds (2020): Los atacantes estuvieron en las redes más de 9 meses antes de ser descubiertos. Las empresas con mejor registro y detección de anomalías pudieron identificar el comportamiento inusual del software de actualización comprometido. Fuente: CISA
Colonial Pipeline (2021): Los atacantes accedieron a la red usando una contraseña VPN comprometida. Una monitorización adecuada de los patrones de autenticación VPN podría haber detectado el acceso no autorizado antes. Fuente: Bloomberg
Taller: configurar el registro y la monitorización
Este taller le guía a través de la implementación de una solución completa de registro y monitorización.
Parte 1: recopilación centralizada de registros
Opción A: Grafana Loki (recomendado para equipos pequeños)
- Cree docker-compose.yml:
version: '3.8'
services:
loki:
image: grafana/loki:2.9.3
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- loki-data:/loki
grafana:
image: grafana/grafana:10.3.1
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_AUTH_ANONYMOUS_ENABLED=true
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
promtail:
image: grafana/promtail:2.9.3
volumes:
- ./promtail-config.yaml:/etc/promtail/config.yml
- /var/log:/var/log:ro
- /var/run/docker.sock:/var/run/docker.sock
command: -config.file=/etc/promtail/config.yml
volumes:
loki-data:
grafana-data:
- Cree promtail-config.yaml:
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
host: ${HOSTNAME}
__path__: /var/log/*.log
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ['__meta_docker_container_name']
target_label: container
- Inicie la pila:
docker compose up -d
-
Acceda a Grafana en http://localhost:3000
-
Añada Loki como fuente de datos:
- Vaya a Connections → Data Sources → Add data source
- Seleccione Loki
- URL: http://loki:3100
- Save & Test
Opción B: AWS CloudWatch (para entornos AWS)
- Instale el agente CloudWatch:
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i amazon-cloudwatch-agent.deb
- Cree la configuración:
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/auth.log",
"log_group_name": "security-logs",
"log_stream_name": "{instance_id}/auth"
},
{
"file_path": "/var/log/nginx/access.log",
"log_group_name": "application-logs",
"log_stream_name": "{instance_id}/nginx-access"
},
{
"file_path": "/app/logs/*.json",
"log_group_name": "application-logs",
"log_stream_name": "{instance_id}/app"
}
]
}
}
}
}
- Inicie el agente:
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config \
-m ec2 \
-s \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/config.json
Parte 2: registro de aplicaciones
- Añada registro estructurado a su aplicación:
Ejemplo en Python:
import structlog
import logging
# Configure structlog
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer()
],
wrapper_class=structlog.stdlib.BoundLogger,
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
)
logger = structlog.get_logger()
# Log security events
def login(user_id, ip_address, success):
logger.info(
"user_login",
user_id=user_id,
ip_address=ip_address,
success=success,
event_type="authentication"
)
Ejemplo en Node.js:
const pino = require('pino');
const logger = pino({
level: 'info',
formatters: {
level: (label) => ({ level: label }),
},
timestamp: pino.stdTimeFunctions.isoTime,
});
// Log security events
function login(userId, ipAddress, success) {
logger.info({
event: 'user_login',
user_id: userId,
ip_address: ipAddress,
success: success,
event_type: 'authentication'
});
}
- Pruebe el registro:
# Generate some log entries
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username": "test", "password": "wrong"}'
# Check logs appear in Grafana/CloudWatch
Parte 3: alertas de seguridad
-
Cree una alerta para inicios de sesión fallidos (Grafana):
- Vaya a Alerting → Alert rules → New alert rule
- Consulta:
count_over_time({job="app"} |= "login" |= "success\":false" [5m]) - Condición: Is above 10
- Carpeta: Security
- Evaluación: Every 1m for 5m
- Añada canal de notificación (Slack, email, etc.)
-
Cree una alarma de CloudWatch:
# Create metric filter
aws logs put-metric-filter \
--log-group-name application-logs \
--filter-name failed-logins \
--filter-pattern '{ $.event = "user_login" && $.success = false }' \
--metric-transformations \
metricName=FailedLogins,metricNamespace=Security,metricValue=1
# Create alarm
aws cloudwatch put-metric-alarm \
--alarm-name HighFailedLogins \
--metric-name FailedLogins \
--namespace Security \
--statistic Sum \
--period 300 \
--threshold 20 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789:security-alerts
- Pruebe las alertas:
# Generate failed logins
for i in {1..25}; do
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username": "test", "password": "wrong"}'
sleep 1
done
# Verify alert fired
Parte 4: panel de control de seguridad
Cree un panel de control de Grafana con estos paneles:
-
Resumen de autenticación:
- Total de inicios de sesión (éxito/fallo)
- Tasa de fallos de inicio de sesión a lo largo del tiempo
- IPs fuente principales de fallos
-
Seguridad de aplicaciones:
- Tasas de error
- Tiempos de respuesta de API
- Límites de velocidad alcanzados
-
Infraestructura:
- Conexiones SSH
- Comandos sudo
- Cambios de estado de servicios
JSON de panel de control de ejemplo:
{
"title": "Security Overview",
"panels": [
{
"title": "Failed Logins (24h)",
"type": "stat",
"targets": [
{
"expr": "count_over_time({job=\"app\"} |= \"login\" |= \"success\\\":false\" [24h])"
}
]
},
{
"title": "Login Failures by IP",
"type": "table",
"targets": [
{
"expr": "sum by (ip_address) (count_over_time({job=\"app\"} |= \"login\" |= \"success\\\":false\" [24h]))"
}
]
},
{
"title": "Authentication Events",
"type": "timeseries",
"targets": [
{
"expr": "sum(count_over_time({job=\"app\"} |= \"login\" [5m]))",
"legendFormat": "Total"
},
{
"expr": "sum(count_over_time({job=\"app\"} |= \"login\" |= \"success\\\":false\" [5m]))",
"legendFormat": "Failed"
}
]
}
]
}
Artefactos a producir
Después de este taller, debería tener:
- Pila de registro centralizado — configuración de Docker Compose o CloudWatch
- Integración del registro de aplicaciones — registro estructurado en su código base
- Alertas de seguridad — Al menos 3 alertas configuradas:
- Umbral de inicios de sesión fallidos
- Acceso fuera de horario
- Pico en la tasa de errores
- Panel de control de seguridad — panel de Grafana/CloudWatch con métricas clave
- Runbook de alertas — documentación para responder a cada tipo de alerta
- Política de retención de registros — documento sobre cuánto tiempo se conservan los registros y por qué
Preguntas de autoevaluación
- ¿Por qué es importante el registro centralizado para la seguridad?
- ¿Qué no debe nunca registrar?
- ¿Cuál es la diferencia entre un sistema de gestión de registros y un SIEM?
- ¿Cómo prevenir la fatiga de alertas?
- ¿Qué período de retención de registros requiere PCI-DSS?
- ¿Cómo investigaría un posible compromiso de cuenta usando registros?
- ¿Qué es el registro estructurado y por qué es mejor?
- ¿Cómo garantizar la integridad de los registros para fines forenses?
- Nombre tres eventos de seguridad que deben desencadenar alertas inmediatas.
- ¿Cuál es la diferencia entre el almacenamiento de registros caliente, templado y frío?
Cómo explicarlo a la dirección
Comience con el riesgo: «Ahora mismo, si alguien vulnera nuestro sistema, no lo sabremos durante meses. El tiempo medio que tarda una empresa en detectar una brecha es de 194 días. Con una monitorización adecuada, podemos detectar problemas en minutos.»
Use un ejemplo real: «El mes pasado, [competidor/noticia] sufrió una brecha. No pudieron averiguar qué datos se tomaron porque sus registros estaban incompletos. Estamos configurando el registro para poder responder siempre a esa pregunta.»
Cuantifique el valor: «La detección más rápida significa menos daño. Según IBM, las brechas detectadas en menos de 200 días cuestan 1 millón de dólares menos que las que tardan más. Nuestra monitorización nos alertará de inmediato.»
Aborde el cumplimiento: «Para SOC 2 / ISO 27001 / [estándar relevante], necesitamos registro centralizado con la retención adecuada. Este proyecto nos pone en conformidad.»
Muestre el panel de control: «Esto es lo que estamos monitorizando ahora. Podemos ver cada inicio de sesión, cada acceso a datos sensibles, cada patrón inusual. Antes, éramos ciegos.»
Compare costes: «Estamos usando herramientas de código abierto que cuestan 0€ en licencias de software. El coste principal es mi tiempo para configurarlo — unas 2 semanas. Un producto SIEM costaría 50-100K€/año.»
Recursos y enlaces
Gestión de registros
- Documentación de ELK Stack
- Documentación de Grafana Loki
- Documentación de Graylog
- Documentación de Fluentd
- Documentación de Vector
Registro en la nube
SIEM y monitorización de seguridad
- Documentación de Wazuh
- Documentación de Security Onion
- Reglas Sigma (formato de reglas de detección)
- Marco MITRE ATT&CK
Mejores prácticas
- OWASP Logging Cheat Sheet
- NIST SP 800-92 Guía de gestión de registros de seguridad informática
- Benchmarks CIS para registro
Alertas y respuesta a incidentes
Qué sigue
Esto completa la sección de seguridad en el desarrollo. Ahora puede escribir código más seguro, gestionar secretos adecuadamente, endurecer su pipeline de CI/CD, asegurar contenedores e infraestructura cloud, y detectar actividad sospechosa cuando ocurre.
Próxima sección: cultura de seguridad — el trabajo más difícil de conseguir que el resto del equipo también se preocupe por la seguridad.