A09 Security Logging and Alerting Failures
No puedes responder a lo que no puedes ver. Los logs sin alertas son un archivo; las alertas sin buenos logs son ruido. Ambos deben existir, y ninguno puede ser inyectable ni estar lleno de secretos.
Qué es
Esta categoría cubre el logging, monitoreo y alertas de seguridad ausentes o deficientes: logins y fallos de control de acceso que no se registran, logs que viven solo en la máquina que controla el atacante, eventos sin ninguna alerta detrás y logs que filtran datos sensibles o que pueden falsificarse con la entrada del usuario.
Es el A09 en el :2025, con un nombre que enfatiza la alerta: un log por el que nadie recibe un aviso no es un control.
Cómo funciona el ataque
- Una tanda de con miles de logins fallidos no se registra, o se registra y nunca se revisa.
- Un atacante con un punto de apoyo borra los logs locales; sin copia central, el rastro desaparece.
- : un nombre de usuario que contiene un salto de línea escribe una línea falsa de "login exitoso".
- Los logs registran contraseñas, tokens o números de tarjeta, convirtiendo el almacén de logs en el objetivo más rico.
Código vulnerable
Logging de texto libre con entrada cruda del usuario, un secreto en el mensaje y ningún registro del fallo que importa.
def login(email, password):
print(f"login attempt {email} / {password}") # secreto en el log, posible inyección de saltos de línea
user = authenticate(email, password)
if not user:
return None # fallo no registrado
return userconsole.log("login failed for " + req.body.email + " with token " + req.headers.authorization);La corrección
Registra los eventos de seguridad como datos estructurados con nombres de campo estables: quién, qué, desde dónde, resultado y un . Nunca registres secretos; codifica la entrada del usuario para que no pueda falsificar líneas. Envía los logs fuera del host de inmediato y asocia reglas de alerta a los eventos que importan (picos de fallos, cambios de privilegio, acceso denegado a objetos sensibles).
import json, logging
log = logging.getLogger("security")
def event(action, outcome, **fields):
log.info(json.dumps({"action": action, "outcome": outcome, **fields})) # json.dumps hace escape de los saltos de línea
def login(email, password, ip, request_id):
user = authenticate(email, password)
if not user:
event("login", "failure", email=email, ip=ip, request_id=request_id) # sin contraseña, sin token
return None
event("login", "success", user_id=user.id, ip=ip, request_id=request_id)
return userreq.log.warn({ action: "login", outcome: "failure", email: req.body.email, ip: req.ip }, "auth");
// la redaction de pino mantiene los secretos fuera aunque alguien registre el objeto completo
const logger = pino({ redact: ["req.headers.authorization", "*.password", "*.token"] });- alert: LoginFailureSpike
expr: sum(rate(auth_events_total{action="login",outcome="failure"}[5m])) > 5
for: 5m
labels: { severity: page }
annotations:
summary: "Fallos de login por encima de lo normal: posible credential stuffing"Demuestra la corrección
Prueba que el evento se emite con los campos correctos, que no aparece ningún secreto y que la entrada no puede falsificar una línea. Luego prueba el propio camino de la alerta: reproduce una ráfaga y confirma que alguien recibe la notificación. Una alerta sin probar es una esperanza.
def test_failed_login_is_logged_without_secrets(caplog):
with caplog.at_level("INFO", logger="security"):
login("bob@x.io\nlogin success", "hunter2", "203.0.113.9", "req-1")
text = caplog.text
assert "hunter2" not in text # sin secreto
assert text.count("\n") == 1 # el salto de línea inyectado no creó una línea
assert '"outcome": "failure"' in text
def test_alert_fires_on_burst(alert_harness):
for _ in range(200):
login("bob@x.io", "bad", "203.0.113.9", "r")
assert alert_harness.fired("LoginFailureSpike")Checklist
- Registra: autenticación, fallos de control de acceso, fallos de validación de entrada, acciones privilegiadas y cambios en la configuración de seguridad.
- Usa un formato estructurado consistente; incluye hora, actor, acción, objetivo, resultado, origen y un correlation id.
- Envía los logs rápidamente a un almacén central de solo anexado; restringe quién puede alterarlos o borrarlos.
- No registres secretos, tokens ni números completos de tarjeta o documento; codifica la entrada no confiable.
- Define alertas y un responsable para cada una; escribe la respuesta en un runbook; prueba el camino de la alerta.
- Define una retención que cubra las necesidades legales y de investigación.
conexiones en el mapa
- A09 Security Logging and Alerting FailuresregistraA10 Mishandling of Exceptional Conditions
- A09 Security Logging and Alerting Failuresdetecta el abuso deA01 Broken Access Control
actualizado el 14/10/2026 · fuentes para profundizar · reportar un error
en otros dominios
- Ingeniería de detecciónsecopsel log se vuelve detección versionada
Estudia antes
Desbloquea
pon a prueba lo que entendiste
Cargando preguntas…