pular para o conteúdo
voltar ao mapa
prática
etapa: operate
3 min de leitura

A09 Security Logging and Alerting Failures

Você não consegue responder ao que não consegue ver. Logs sem alertas são um arquivo morto; alertas sem bons logs são ruído. Ambos precisam existir, e nenhum pode ser injetável nem estar cheio de segredos.

OpenTelemetryLoki / Elastic / SplunkSigma rulesFalcoWazuhfail2ban

O que é

Esta categoria cobre logging, monitoramento e alertas de segurança ausentes ou ruins: logins e falhas de controle de acesso que não são registrados, logs que ficam apenas na máquina que o atacante controla, eventos sem nenhum alerta por trás e logs que vazam dados sensíveis ou podem ser forjados pela entrada do usuário.

É o A09 no :2025, com um nome que enfatiza o alerta: um log sobre o qual ninguém é acionado não é um controle.

Como o ataque funciona

  • Uma rodada de com milhares de logins falhos não é registrada, ou é registrada e nunca revisada.
  • Um atacante com acesso inicial apaga os logs locais; sem cópia central, o rastro desaparece.
  • : um nome de usuário contendo uma quebra de linha escreve uma linha falsa de "login com sucesso".
  • Os logs registram senhas, tokens ou números de cartão, transformando o repositório de logs no alvo mais rico.

Código vulnerável

Logging de texto livre com entrada bruta do usuário, um segredo na mensagem e nenhum registro da falha que importa.

login.pypython
def login(email, password):
    print(f"login attempt {email} / {password}")          # segredo no log, possível injeção de quebra de linha
    user = authenticate(email, password)
    if not user:
        return None                                        # falha não registrada
    return user
login.jsjavascript
console.log("login failed for " + req.body.email + " with token " + req.headers.authorization);

A correção

Registre os eventos de segurança como dados estruturados com nomes de campo estáveis: quem, o quê, de onde, resultado e um . Nunca registre segredos; codifique a entrada do usuário para que não possa forjar linhas. Envie os logs para fora do host imediatamente e associe regras de alerta aos eventos que importam (picos de falhas, mudanças de privilégio, acesso negado a objetos sensíveis).

login.pypython
import json, logging
log = logging.getLogger("security")

def event(action, outcome, **fields):
    log.info(json.dumps({"action": action, "outcome": outcome, **fields}))   # json.dumps faz escape de quebras de linha

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)   # sem senha, sem token
        return None
    event("login", "success", user_id=user.id, ip=ip, request_id=request_id)
    return user
login.js (pino)javascript
req.log.warn({ action: "login", outcome: "failure", email: req.body.email, ip: req.ip }, "auth");
// a redaction do pino mantém os segredos fora mesmo que alguém registre o objeto inteiro
const logger = pino({ redact: ["req.headers.authorization", "*.password", "*.token"] });
alert.yml (regra no estilo Prometheus)yaml
- alert: LoginFailureSpike
  expr: sum(rate(auth_events_total{action="login",outcome="failure"}[5m])) > 5
  for: 5m
  labels: { severity: page }
  annotations:
    summary: "Falhas de login acima do normal: possível credential stuffing"

Prove a correção

Teste que o evento é emitido com os campos certos, que nenhum segredo aparece e que a entrada não consegue forjar uma linha. Depois teste o próprio caminho do alerta: reproduza uma rajada e confirme que alguém é notificado. Um alerta não testado é uma esperança.

test_logging.py (pytest)python
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                      # sem segredo
    assert text.count("\n") == 1                       # a quebra de linha injetada não criou uma linha
    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

  • Registre: autenticação, falhas de controle de acesso, falhas de validação de entrada, ações privilegiadas e mudanças na configuração de segurança.
  • Use um formato estruturado consistente; inclua hora, ator, ação, alvo, resultado, origem e um correlation id.
  • Envie os logs rapidamente a um repositório central somente de anexação; restrinja quem pode alterá-los ou apagá-los.
  • Não registre segredos, tokens nem números completos de cartão/documento; codifique a entrada não confiável.
  • Defina alertas e um responsável para cada um; escreva a resposta em um runbook; teste o caminho do alerta.
  • Defina uma retenção que atenda às necessidades legais e de investigação.
OWASP Top 10A lista de referência mais famosa de riscos de segurança em aplicações web (A01 acesso quebrado, A02 falhas criptográficas, A03 injeção...), revisada a cada alguns anos pela comunidade OWASP. É um mapa educacional de categorias de risco — não um checklist de ferramenta nem uma certificação.ver no glossário →Credential stuffingAtaque que testa em massa pares usuário/senha vazados de outros serviços. Assinatura: muitas falhas de login distribuídas por muitos IPs, com poucas tentativas por conta.ver no glossário →Log injectionEntrada de usuário contendo quebras de linha ou caracteres de controle forja entradas de log. Codifique a entrada ou use logging estruturado (JSON).ver no glossário →Correlation idrequest idUm id único anexado a uma requisição e a cada linha de log que ela gera, exibido ao usuário nas mensagens de erro para que o suporte encontre os detalhes sem vazá-los.ver no glossário →

conexões no mapa

atualizado em 14/10/2026 · fontes para aprofundar · reportar erro

em outros domínios

Estude antes

Desbloqueia

teste o que entendeu

Carregando perguntas…