Logs em Java: o que registrar e o que não registrar?
11 de novembro de 2020
0 minutos de leituraOs logs são uma ferramenta útil para identificar erros e depurar código. Para engenheiros e, mais especificamente, em ambientes de DevOps, eles são muito valiosos.
Além de sua função operacional, os logs também são fundamentais para a segurança em Java. Quando ocorre uma violação de segurança, os arquivos de log são o primeiro lugar onde procurar pistas sobre o que aconteceu. Mas, com tantos frameworks disponíveis para registrar logs em aplicações Java, como escolher o mais adequado? E, mais importante, como registrar logs em Java da maneira correta?
A qualidade dos logs é essencial. Registrar informações demais ou de menos pode ser catastrófico do ponto de vista da segurança em Java. Na verdade, o registro e monitoramento insuficientes (A10:2017) fazem parte da lista OWASP Top 10.
Neste artigo, vou apresentar uma abordagem prática para registrar logs em Java: o que devemos registrar, o que não devemos e como implementar esse processo corretamente.
O que devemos registrar?
Quando eu servia no exército, sempre que precisava relatar algo, usávamos o método 5W-H — Quem, O quê, Onde, Quando, Por quê e Como. Esse método também é uma excelente regra para criar mensagens de log.
Quem fez isso?
Identifique o usuário ou sistema que fez a solicitação. Se possível, registre o ID do usuário e o endereço IP.
O que está acontecendo?
Que tipo de mensagem de log é essa? Muitos desenvolvedores já conhecem os diferentes níveis de log usados para indicar a importância de uma mensagem (ERROR, WARN, INFO, DEBUG, TRACE). É recomendável distinguir entre logs funcionais, de segurança e, quando aplicável, de auditoria.
Onde aconteceu?
Identifique o local. Geralmente, você define um logger por classe, o que já é um bom começo. No entanto, também precisamos saber qual método específico está envolvido. Além disso, se você estiver trabalhando com uma arquitetura de microsserviços, é importante identificar o nó em questão.
Quando aconteceu?
O horário é essencial, então inclua um timestamp nos logs. Leve em conta as diferenças de fuso horário e, principalmente, as mudanças de horário que ocorrem duas vezes por ano. Recomendo sempre usar UTC para que todos entendam o horário.
Por que aconteceu?
Em geral, é isso que você precisa descobrir. Nem sempre dá para registrar o motivo em uma única linha de log. Às vezes, ele é evidente. Nesse caso, inclua-o na mensagem. Muitas vezes, só conseguimos explicar o “porquê” ao relacionar diferentes logs. Por isso, deixe as mensagens de log sempre muito claras.
Como aconteceu?
Assim como o “porquê”, o “como” geralmente não pode ser registrado em uma única mensagem. Por isso, correlacionar mensagens é extremamente importante, sobretudo em uma arquitetura baseada em serviços. Para isso, crie um ID de correlação exclusivo para cada solicitação recebida por cada serviço com uma interface de saída. Depois, registre esse ID em todas as mensagens. Se precisar enviá-lo a outro serviço, você pode usar o cabeçalho HTTP específico X-Correlation-ID para correlacionar as solicitações sem manter estado.
Registro de eventos de segurança
Java é usado principalmente em processos de back-end e interage diretamente com os dados. Por isso, precisamos acompanhar os eventos relacionados à segurança. É preciso priorizá-los de acordo com a importância, dando atenção especial aos que exigem privilégios elevados, como eventos de administrador.
Em geral, considere registrar:
logins bem-sucedidos e malsucedidos
logouts
alterações e redefinições de senha
criação e remoção de usuários
falhas de autorização
alterações nos níveis de acesso
Além disso, registre indicadores que possam apontar para ataques específicos.
erros de validação de entrada: identifique tags de script que possam levar a ataques XSS ou parâmetros como UNION, que indicam uma injeção de SQL
respostas HTTP 4xx e 5xx
quantidades absurdas de solicitações vindas de um mesmo endereço IP
verifique os intervalos de IP, por exemplo, se um serviço interno receber solicitações de um endereço IP externo
Uma ótima estratégia é definir limites para determinados eventos de segurança registrados. Você pode enviar alertas automaticamente quando esses limites forem ultrapassados. Assim, é possível agir de acordo e evitar danos ainda maiores.
O que não devemos registrar?
Registrar informações demais é tão prejudicial quanto registrar de menos. Você não quer encher os logs com informações inúteis em produção. Por isso, defina sempre um nível de log adequado para os sistemas de produção, como WARN ou ERROR.
Durante o desenvolvimento, os engenheiros podem registrar informações confidenciais que contenham dados pessoais para depurar aplicações. Embora devamos evitar isso, mensagens internas de depuração precisam usar o nível de log correto — por exemplo, DEBUG ou TRACE — e não devem ficar visíveis no ambiente de produção.
Como regra geral, uma aplicação não deve revelar dados específicos da aplicação ou dos usuários, pois um invasor pode usá-los. Além disso, ao expor informações pessoais, é muito provável que você deixe de cumprir as normas de privacidade. Mesmo que apenas pessoas internas tenham acesso aos logs, eles representam um ponto único de falha. Portanto, não queremos que essas informações caiam em mãos erradas.
Durante o desenvolvimento, pense no que você realmente precisa registrar. Precisamos incluir um ID interno, a senha sem criptografia ou os dados do cartão de crédito de alguém? Tenha cuidado para não exibir um objeto de dados inteiro em Java, pois a saída depende do método toString() desse objeto.
Ao registrar informações pessoais para depuração, contornamos muitas das nossas próprias políticas de segurança. O mais sensato é não registrar esse tipo de informação. Se for necessário registrar dados específicos, use o nível de log adequado, como DEBUG. Também defina um nível mais alto para os sistemas de produção, como WARN.
Em resumo: informações pessoais e confidenciais não devem aparecer nos logs de produção.
Logs de segurança em Java
Antes de tudo, use um framework de logging em Java. Parece óbvio, mas muitas aplicações ainda usam o antigo System.out.println() para escrever diretamente no console. Isso deve ser evitado a todo custo. Um bom linter, como Spotbugs, PMD ou SonarLint, pode ajudar.
Há muitos frameworks de logging diferentes para Java. Este artigo aborda Logback e Log4j2, que atualmente são os mais usados. Mas, independentemente do framework, é recomendável separar os logs de segurança dos logs gerais da aplicação. No Logback e no Log4j2, você pode fazer isso facilmente adicionando marcadores ao logger. Com um marcador específico, como SECURITY ou até AUDIT, você pode filtrar esses logs com facilidade. Outra opção é adicionar um appender extra ao logger e filtrar pelo marcador específico para armazenar ou exibir esses logs separadamente.
Logback
Para implementar o Logback, basta incluir a dependência logback-classic, como mostrado abaixo. Ela também inclui os pacotes logback-core e slf4j-api.
Marcadores
Você pode definir marcadores usando qualquer String e adicioná-los ao logger, independentemente do nível de log, como mostrado abaixo.
Configuração
No exemplo a seguir de configuração do Logback, criei dois appenders no arquivo logback.xml.
O primeiro appender exibe todas as linhas de log no console, inclusive os marcadores que eu tiver adicionado. O segundo filtra apenas os logs de segurança com base no marcador SECURITY e os grava em um arquivo separado a cada dia.
Log4j2
Para usar o Log4j2, inclua os pacotes log4j-api e log4j-core, como mostrado abaixo.
Marcadores
A criação e a inclusão de marcadores são semelhantes ao exemplo do Logback. A diferença é que, desta vez, usamos o gerenciador específico do Log4j para criar o logger e o marcador.
Configuração
Assim como na configuração do Logback, criei uma configuração para o Log4j2 com dois appenders, que armazenam os logs de segurança em um arquivo separado com base nos marcadores. Essa configuração deve estar no arquivo log4j2.xml no classpath.
Logs em sistemas distribuídos
Em sistemas distribuídos, é melhor não gravar os logs em um único arquivo por serviço. Prefira armazená-los em um local centralizado. Isso facilita relacionar as linhas de log e encontrar possíveis problemas.
Dica profissional: ao implementar o logging centralizado, é útil configurar as linhas de log no formato JSON. Assim, indexar e pesquisar fica mais fácil e rápido.
Além do logging centralizado, considere também monitorar ativamente as linhas de log e configurar alertas. Vários produtos podem gerar gráficos em tempo real com base nos seus logs e enviar alertas automaticamente quando os valores ultrapassarem um limite predefinido.
Conclusão
Registrar logs não é algo que se faz sem pensar. Analisar com atenção o que você registra, quando registra e como utiliza os logs é essencial para investigar problemas e agir rapidamente. A prevenção é melhor do que a solução, mas ainda é possível que alguém invada seu sistema — e você vai querer evitar danos ainda maiores.
Se esse momento difícil chegar, garanta que seus logs possam ajudar você!
Comece a jogar Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.



