← Blog
·11 min read

OWASP A09 Registro y Monitoreo

OWASP A09 explica por qué las brechas tardan 204 días en detectarse. Aprende qué registrar, qué nunca guardar en logs, y cómo corregir los fallos silenciosos en tu app.

Rod

Founder & Developer

El tiempo promedio para detectar una brecha de seguridad es 204 días, según el reporte de IBM Cost of a Data Breach. O sea, un atacante puede vivir dentro de tu aplicación durante seis meses antes de que alguien se dé cuenta. La razón no es que sean super sofisticados — es OWASP A09:2021, Fallos de Registro y Monitoreo de Seguridad. Sin logs, sin alertas, sin manera de saber que algo está mal.

La mayoría de las categorías del OWASP Top 10 tratan de prevenir ataques. A09 es diferente. Trata de lo que pasa después de que algo sale mal. Cuando tu logging falla, todos los demás controles de seguridad también fallan — porque no hay registro de que alguien intentó algo, no hay señal de que algo inusual está pasando, y no hay forma de reconstruir qué hizo el atacante.

Esta guía cubre los fallos de logging concretos que aparecen en codebases reales — los que vemos cuando escaneamos repos en Data Hogo — y cómo corregirlos.


Cómo Se Ven los Fallos de OWASP A09 en la Práctica

Esta es la categoría de OWASP que los devs menos consideran hasta que algo sale mal. Esto es lo que realmente cubre.

Sin Registro de Autenticaciones Fallidas

Tu app probablemente registra algo cuando las cosas se rompen. ¿Pero registra cuando alguien falla al iniciar sesión? La mayoría no lo hace.

Un login fallido es normal — el usuario escribió mal su contraseña. Cincuenta logins fallidos contra la misma cuenta en dos minutos es un ataque de credential stuffing. Sin un log de cada fallo, no puedes distinguirlos. El ataque pasa en silencio, y si tiene éxito, el login exitoso se ve completamente normal.

Esto es logging de seguridad insuficiente — el hallazgo A09 más común que vemos.

Registrar Datos Sensibles

Este va en la dirección contraria: estás registrando demasiado. Específicamente, estás guardando en logs datos que nunca deberían estar ahí.

El caso más común es loguear el body completo de las requests durante el desarrollo y nunca quitar ese log:

// MAL: Registra la contraseña en texto plano — cualquiera con acceso a los logs puede robar credenciales
app.post('/login', (req, res) => {
  console.log('Intento de login:', req.body); // { email: "user@example.com", password: "miContrasena123" }
  // ...
});

Hemos encontrado este patrón en codebases en producción activamente corriendo. El dev agregó el log durante el desarrollo y lo pusheó. Ha estado ahí meses, escribiendo la contraseña de cada usuario en stdout.

El problema de console.log con datos sensibles es más amplio que las contraseñas. Los objetos de usuario completos contienen tokens. Los headers de requests contienen valores de Authorization. Las variables de entorno dumpeadas en logs contienen API keys. Cualquiera de estas cosas en tu archivo de log — que puede estar almacenado en texto plano, indexado por tu proveedor de logging, y accesible para múltiples personas del equipo — es una exposición real.

Log Injection

Esta la mayoría de los devs nunca la han escuchado. Log injection ocurre cuando construyes cadenas de log concatenando input del usuario, y el atacante incluye saltos de línea en su input para falsificar entradas en el log.

// MAL: El atacante controla el valor de 'username'
// Si username = "alice\nINFO 2026-02-28T10:00:00Z event=login_success user=admin"
// ...el archivo de log ahora contiene una entrada falsa de "admin inició sesión"
logger.info('Usuario inició sesión: ' + username);

Suena abstracto, pero tiene consecuencias reales. Un atacante puede usar log injection para ocultar sus movimientos creando ruido en el archivo de log, para falsificar entradas de "éxito" que hagan que los sistemas de detección de intrusiones ignoren su actividad, o para inyectar contenido malicioso en archivos de log que luego son procesados por otras herramientas. Ve la entrada completa de vulnerabilidad de log injection para más ejemplos.

console.log en Producción Sin Agregación

Usar console.log en producción no es inherentemente malo si escribes salida estructurada que se captura correctamente. Pero en la práctica suele significar dos cosas: o es texto no estructurado que va a stdout y se pierde, o es una instrucción de depuración que nunca se pensó para producción y contiene algo sensible.

Console.log en producción sin un sistema de agregación centralizada significa que tus logs no son consultables, no generan alertas, y frecuentemente no se retienen. Cuando algo sale mal, no tienes historial para revisar.

Sin Monitoreo de Errores

Tu app lanza excepciones. Todas las apps lo hacen. La pregunta es si sabes cuándo pasa.

Sin monitoreo de errores, las excepciones no manejadas fallan en silencio, las excepciones capturadas se tragan sin registro, y solo te enteras de que algo está roto cuando un usuario te manda un correo. Desde una perspectiva de seguridad, esto importa porque los errores frecuentemente señalan ataques. Los intentos de inyección SQL disparan errores de base de datos. Los intentos de path traversal disparan errores del sistema de archivos. La autenticación rota dispara excepciones en tu librería de JWT.

Si no estás monitoreando errores en producción, no estás monitoreando ataques.

Variables de Entorno en los Logs

Esta es suficientemente específica como para tener su propia categoría: variables de entorno en logs. Pasa cuando dumpeas process.env para depuración, o cuando una librería que usas loguea su configuración al arrancar, y tu entorno incluye credenciales.

// MAL: Dumpea TODAS las variables de entorno al iniciar — incluidas las secrets
console.log('Iniciando con configuración:', process.env);

Si esta línea corre en producción, cada API key, URL de base de datos y token secreto en tu entorno está ahora en tu archivo de log.


Cómo Se Ve un Logging de Seguridad Correcto

Ahora las correcciones. El principio central es este: usa logging estructurado, registra eventos no cadenas de texto, y nunca dejes que el input del usuario toque directamente el formato de tu log.

Cambia de console.log a un Logger Estructurado

La diferencia entre console.log y un logger estructurado no es solo estética. Los logs estructurados son consultables, filtrables y legibles por máquinas. Tu sistema de monitoreo puede generar alertas en event: "login_failed" con un conteo. No puede hacer eso con "Login fallido para bob@example.com".

// MAL: No estructurado, no consultable, y registra la contraseña
console.log('Login fallido para', email, password);
// BIEN: Estructurado, consultable, campos sensibles redactados
import pino from 'pino';
 
const logger = pino({ level: 'info' });
 
function redactarEmail(email: string): string {
  // Conserva el dominio para depuración, redacta la parte local
  const [, domain] = email.split('@');
  return `***@${domain}`;
}
 
logger.warn({
  event: 'login_failed',
  email: redactarEmail(email), // nunca loguear el email completo si es posible
  ip: req.ip,
  userAgent: req.headers['user-agent'],
  timestamp: new Date().toISOString(),
});

Pino es la elección correcta para Node.js — es el logger estructurado más rápido disponible y su salida es JSON nativo. Winston también es sólido si necesitas más flexibilidad en transportes.

Registra Eventos, No Dumps de Objetos

La tentación es loguear el objeto completo. No lo hagas.

// MAL: Dumpea el objeto usuario completo — incluye tokens, contraseñas hasheadas, IDs internos
console.log('Usuario autenticado:', user);
// BIEN: Registra solo los campos necesarios para depuración y monitoreo
logger.info({
  event: 'login_success',
  userId: user.id,
  ip: req.ip,
  // Nada más. Ni user.email, ni user.token, ni user.passwordHash
});

La regla es simple: loguea el ID de una cosa, no la cosa en sí. Si necesitas más contexto después, puedes consultar la base de datos por ID.

Corrige Log Injection con Campos Estructurados

El logging estructurado también resuelve log injection. Cuando el input del usuario es un valor de campo en lugar de parte de la cadena de log, no puede escaparse al formato del log.

// MAL: Concatenación de strings — username puede contener saltos de línea y falsificar entradas
logger.info('Usuario inició sesión: ' + username);
 
// BIEN: Campo estructurado — username es solo un valor, no afecta el formato del log
logger.info({ event: 'login_success', username: username });

Cuando username es un campo en un objeto JSON, si el atacante lo hace "alice\nINFO: ...", se almacena como el valor de string alice\nINFO: ... — no como una segunda entrada de log. La estructura JSON previene la inyección.

El Patrón Completo para Logging de Eventos de Auth

Así se ve una implementación completa de logging de autenticación:

// auth-logger.ts — registra cada evento de auth relevante para seguridad
import pino from 'pino';
 
const logger = pino({ level: 'info' });
 
export function logAuthEvent(event: {
  type: 'login_success' | 'login_failed' | 'logout' | 'password_reset' | 'mfa_failed';
  userId?: string;  // undefined en login_failed (el usuario puede no existir)
  ip: string;
  userAgent?: string;
}) {
  // Usar 'warn' para fallos — no son errores, pero necesitan atención
  logger.warn({
    ...event,
    timestamp: new Date().toISOString(),
  });
}
 
// En tu handler de login:
if (!passwordMatches) {
  logAuthEvent({ type: 'login_failed', ip: req.ip, userAgent: req.headers['user-agent'] });
  return res.status(401).json({ error: 'Credenciales inválidas' });
}
 
logAuthEvent({ type: 'login_success', userId: user.id, ip: req.ip });

Cada intento de login fallido debe generar una entrada de log. Este es el mínimo para cumplir OWASP A09, y son los datos que necesitas para detectar ataques de fuerza bruta y credential stuffing.


Qué Registrar — y Qué Nunca Tocar

Registra Estos Eventos

Categoría Eventos
Autenticación Login exitoso, login fallido, logout, solicitud de reset de contraseña, fallo de MFA
Autorización Respuestas 403, permiso denegado, intentos de escalada de roles
Validación de inputs Request rechazada por input inválido (pero no el input en sí)
Acciones de admin Usuario creado/eliminado, configuración cambiada, roles modificados
Errores del servidor Excepciones no manejadas, respuestas 500, fallos de conexión a la base de datos
Rate limiting Requests bloqueadas, con IP y endpoint

Nunca Registres Esto

Categoría Por qué
Contraseñas (texto plano o hash) El texto plano es obvio. Las contraseñas hasheadas se pueden crackear offline si las roban.
Tokens de sesión / JWTs Un token en un log es un token que puede ser reutilizado.
API keys y secretos Los logs suelen almacenarse en sistemas menos seguros que tu secrets manager.
Números de tarjeta de crédito completos Violación de PCI-DSS. Loguea solo los últimos 4 dígitos.
Body completo en endpoints de auth Contiene contraseñas. Registra que ocurrió un request, no su contenido.
Dumps de process.env Contiene todos tus secretos.
PII más allá de lo necesario Ver PII en logs — GDPR y leyes de privacidad de LATAM tienen algo que decir al respecto.

Casos Reales: Cuando el Logging Falla a Gran Escala

La brecha de Equifax en 2017 es el caso de estudio para fallos de A09. Los atacantes explotaron una vulnerabilidad conocida en Apache Struts (CVE-2017-5638) y permanecieron en la red de Equifax durante 76 días antes de ser detectados. En ese tiempo, exfiltraron los registros personales de 147 millones de personas.

La razón por la que tardó 76 días no fue la sofisticación del ataque — fue que las herramientas de monitoreo no estaban configuradas correctamente, un certificado SSL expirado impedía la inspección del tráfico, y las alertas que sí dispararon se perdieron en el ruido y no se actuó sobre ellas.

La brecha de Target en 2013 es un fallo diferente: el monitoreo funcionó. Las herramientas de seguridad detectaron la intrusión y dispararon alertas. Esas alertas fueron revisadas por el equipo de seguridad — e ignoradas, porque el alert fatigue había vuelto al equipo insensible a ellas. La brecha no fue un fallo técnico de monitoreo, fue uno operacional.

Ambos ejemplos apuntan a la misma brecha: el logging y el monitoreo no son un checkbox, son una práctica operacional. Necesitas logs, necesitas alertas que disparen en umbrales significativos, y necesitas alguien que actúe sobre esas alertas.


Prevención: Construir Logging de Seguridad Que Realmente Funcione

1. Usa un Logger Estructurado desde el Principio

Instala Pino o Winston desde el día uno, no después de un incidente. El hábito de logging estructurado previene simultáneamente el problema de "loguear datos sensibles" y el de "sin logs consultables".

2. Envía los Logs a un Sistema Centralizado

stdout no es un sistema de logs. Envía tus logs a un servicio que pueda generar alertas, agregarlos y retenerlos. Opciones que funcionan bien para devs independientes y equipos pequeños:

  • Sentry — el mejor para monitoreo de errores, el tier gratuito es generoso. El problema de sin monitoreo de errores se resuelve en unos 10 minutos con Sentry.
  • Datadog — observabilidad más completa, más cara. Vale la pena a escala.
  • Logtail / Better Stack — agregación de logs estructurados asequible. Buena opción para equipos pequeños.
  • Logs integrados de Railway / Vercel — insuficientes por sí solos (sin alertas, retención limitada), pero un buen punto de partida.

3. Configura Reglas de Alerta en Eventos de Auth

Una vez que estés registrando eventos de auth, configura alertas. El conjunto mínimo:

  • Más de 10 logins fallidos desde la misma IP en 5 minutos
  • Más de 5 logins fallidos contra la misma cuenta en 10 minutos
  • Cualquier login desde un país del que tus usuarios normalmente no vienen
  • Acción de admin realizada fuera de horario laboral (opcional, pero detecta bastante)

4. Audita tus Logs Existentes para Datos Sensibles

Revisa tu salida de logs actual antes de continuar. Busca patrones que sugieran exposición de credenciales:

// Patrones que buscar en la salida de tus logs o archivos de log:
// - "password" / "contraseña"
// - "token"
// - "Authorization:"
// - "SECRET" / "KEY"
// - "process.env"

Si encuentras cualquiera de estos como valores (no como claves de objeto), tienes un fallo de logging de seguridad corriendo activamente en producción ahora mismo.

5. Nunca Loguees Input del Usuario Directamente

Cada vez que estés a punto de loguear algo que vino del usuario, pregúntate: ¿es esto un campo en una entrada de log estructurada, o se está concatenando a una cadena? Si es lo segundo, corrígelo. Si genuinamente necesitas loguear el input del usuario para depuración (nunca en producción), redáctalo o trunca primero.


Cómo Data Hogo Detecta Fallos de A09

Cuando escaneamos un repo en Data Hogo, los checks de A09 buscan patrones de código específicos:

  • Llamadas a console.log en rutas que manejan autenticación o datos sensibles
  • Concatenación de strings en llamadas de log que incluyen variables (riesgo de log injection)
  • process.env pasado a cualquier función de logging
  • Middleware de manejo de errores faltante en apps Express/Next.js
  • req.body o req.headers logueados en endpoints de auth

También verificamos la ausencia de librerías de logging estructurado en package.json — un proyecto sin Pino, Winston, o similar casi seguramente depende de console.log en producción.

Estos checks mapean directamente a las entradas de fallos de logging de seguridad, log injection, console.log datos sensibles, PII en logs, variables de entorno en logs, y sin monitoreo de errores en nuestra enciclopedia de vulnerabilidades.

Ve qué fallos de logging encuentra Data Hogo en tu repo — escanea gratis.


OWASP A09 y el Panorama General de Seguridad

A09 no existe en aislamiento. Es la categoría que determina si sabes sobre las otras nueve. Un problema de control de acceso roto (A01) es detectable si estás logueando respuestas 403. Un ataque de inyección (A03) dispara errores de base de datos. La deserialización insegura (A08) causa excepciones.

Sin logs, todos estos ataques pueden tener éxito y pasar desapercibidos. Por eso existe el número de 204 días de IBM. Los atacantes no son invisibles — simplemente están operando en un entorno donde nadie está mirando.

La corrección no es complicada. Logging estructurado con Pino toma 20 minutos configurar. Enviar logs a Sentry toma otros 10. Escribir unas reglas de alerta toma una tarde. Nada de esto requiere experiencia en seguridad — requiere tratar la observabilidad como algo importante desde el inicio del proyecto.

Si quieres saber dónde está parada tu app ahora mismo, los checks de OWASP A09 en nuestra enciclopedia de seguridad son un buen punto de partida. O ve directo a un escaneo y ve los hallazgos reales en tu codebase.

Escanea tu repo gratis — encuentra tus brechas de A09 en menos de 60 segundos →


Preguntas Frecuentes

¿Qué es OWASP A09 Fallos de Registro y Monitoreo de Seguridad?

OWASP A09:2021 cubre la falla al no registrar, monitorear o alertar sobre eventos relevantes de seguridad en una aplicación. Sin logging suficiente, los atacantes pueden operar sin ser detectados durante meses. Incluye la ausencia de logs para autenticaciones fallidas, falta de alertas ante actividad sospechosa, registro de datos sensibles como contraseñas, y la ausencia total de monitoreo de errores en producción.

¿Qué debo registrar en los logs de seguridad?

Registra eventos de autenticación (logins exitosos y fallidos, logouts, resets de contraseña), fallos de autorización (403, permisos denegados), fallos de validación de inputs, acciones administrativas y todos los errores del servidor. Para cada evento captura timestamp, ID de usuario o IP, tipo de evento y resultado. Nunca registres contraseñas, tokens, API keys ni credenciales.

¿Es seguro usar console.log en producción?

No. console.log en producción es un riesgo de seguridad por dos razones: primero, los devs suelen loguear objetos completos que contienen tokens, contraseñas o datos personales; segundo, la salida de consola no tiene estructura, así que no se puede consultar, generar alertas ni agregar. Usa un logger estructurado como Pino o Winston, y envía los logs a un sistema centralizado como Sentry o Datadog.

¿Qué es log injection y cómo prevenirlo?

Log injection ocurre cuando un atacante incluye saltos de línea o caracteres de control en su input, y ese input se escribe directo en tu archivo de log, creando entradas falsas. Por ejemplo, un username de admin\nINFO: user=admin logged in crearía una entrada falsa de login exitoso en tu log. Para prevenirlo, nunca concatenes input del usuario en cadenas de log — usa logging estructurado con campos separados para cada valor.

¿Cuánto tiempo tarda en detectarse una brecha de seguridad en promedio?

Según el reporte de IBM Cost of a Data Breach 2024, el tiempo promedio para identificar y contener una brecha es de 204 días. Este número baja significativamente cuando hay monitoreo y alertas de seguridad activos. El factor más grande en esos 204 días es la ausencia de logs significativos y alertas — exactamente lo que OWASP A09 aborda.

OWASPseguridadloggingmonitoreoNode.jsvibe-codingseguridad-aplicaciones