Banco de Dados

12 jun, 2015

Usando logs de acesso do servidor web como um sistema de armazenamento de banco de dados

Publicidade

Eu usei esse truque há alguns dias, quando lancei o Drupal EngineHack de detecção de sites, e ele está servindo ao seu propósito muito bem.

Meu caso de uso

O EngineHack verifica um site e informa ao usuário se ele foi hackeado ou não. Assim, para cada ferramenta em particular, quero registrar os resultados dessas verificações. Mais importante, eu queria fazer o registro de itens específicos:

  • O timestamp de cada verificação que foi realizada
  • A URL do site que foi verificado
  • Se o servidor Drupal foi comprometido ou não
  • Opcionalmente, o IP do site que efetuou a verificação, para o caso de abuso ou ataque DoS

Tradicionalmente, eu iria criar uma tabela em um banco de dados como MySQL ou um sistema de armazenamento simples por chave/valor como o MongoDB e armazenar os resultados obtidos lá. Mas eu não queria gastar muito tempo lidando com injeção de SQL, validação de dados etc.

E armazenar coisas dentro de uma tabela MySQL nem sempre é tão prático: eu não tinha uma interface gráfica, então tudo tinha de ser feito no terminal, via linha de comando. Criação de tabelas, inclusão de dados, consultas etc. Tudo soava como um monte de trabalho desnecessário para uma ferramenta tão simples.

Deve haver um jeito mais fácil, certo?

Logs de acesso como método de armazenamento

Tudo o que você faz em um navegador é registrado no servidor web. Cada timestamp, cada URL e todos os parâmetros GET. Eu posso usar isso para servir o meu propósito!

access_logs_everywhere

A maioria dos dados que eu queria registrar já estava presente nos logs do servidor. Eu tinha o timestamp e o IP de cada uma das verificações.

Tudo o que restou foi a URL do site Drupal que tinha sido verificado e o resultado: comprometido, sim ou não.

Resolvi incluir um pixel escondido na página de resultados. A URL foi assim.

<img src="/check_pixel.png?url=http://www.domain.tld&compromised=false"
     width="1px"
     height="1px"
/>

Ninguém percebe isso no browser. É o mesmo tipo de técnica que muitos rastreadores de conteúdo usam em ferramentas diversas para verificar as taxas de abertura de boletins informativos ou de acesso a determinados conteúdos.

Tudo que eu tinha que fazer agora era verificar meus logs de acesso para solicitações GET em busca daquele arquivo .png em especial e eu tinha tudo: o IP, o timestamp, o site que foi verificado e qual foi o resultado.

...
10.0.1.1 [28/Apr/2015:11:19:54] "GET /check_pixel.png?url=http://some.domain.tld&compromised=false HTTP/1.1" 200 901
10.0.1.1 [28/Apr/2015:11:20:05] "GET /check_pixel.png?url=http://www.domain.tld&compromised=true HTTP/1.1" 200 901 
...

Perfeito!

Consultando o conjunto de dados

Em longo prazo, criar uma instrução SQL é mais fácil do que isso. Mas, uma vez que eu faço tudo principalmente via linha de comando, isso parece mais natural para mim.

Quantos sites foram digitalizados?

$ grep 'check_pixel.png' scan_results.log | sort | uniq | wc -l
843

Note que eu não estou usando o comando grep -c para contar os registros, uma vez que alguns sites já foram verificados várias vezes, e eu quero apenas os valores exclusivos.

Quantos sites foram comprometidos?

$ grep 'compromised=true' scan_results.log | sort | uniq | wc -l
9

Quais sites foram verificados?

$ awk '{print $7}' scan_results.log | sort | uniq
...
/check_pixel.png?url=http://domain.tld&compromised=false
/check_pixel.png?url=http://otherdomain.Tld&compromised=false
...

Quais sites foram comprometidos?

$ awk '{print $7}' /var/www/enginehack.ma.ttias.be/results/scan_results.log | grep 'compromised=true' | sort | uniq
...
/check_pixel.png?url=http://domain.tld&compromised=true
/check_pixel.png?url=http://otherdomain.Tld&compromised=true
...

Eu tenho todos os resultados de que preciso, bem ali no terminal.

Logrotate

Uma desvantagem desse sistema é que ele não tem várias das propriedades ACID. O mais importante, pelo menos para mim, é a durabilidade.

Eu tenho o logrotate configurado para executar todos os logs do servidor toda noite e armazená-los durante 7 dias. Isso significaria meus logs de verificação também seriam excluídos depois de 7 dias, uma vez que os logs de acesso do servidor web são apagados após esse período.

Um script simples cuida disso.

#!/bin/bash
grep 'check_pixel.png' /path/to/access.log >> /path/to/permanent/logs/scan_results.log

Esse script é executado toda noite, antes do logrotate e leva os resultados do log atual para um arquivo permanente, com segurança, e os mantêm disponíveis para minhas consultas. Fácil.

Vantagens e desvantagens

Para mim, essa técnica funcionou perfeitamente. Meus benefícios:

  • Nenhum código complexo para armazenar dados em tabelas do MySQL é necessário
  • Resolvi a questão de concorrência de arquivos para registrar os resultados para o servidor web
  • Usei uma tecnologia comprovada para obter as solicitações de registro

Eu poderia conviver com as desvantagens também.

  • Não há consultas fáceis de dados, tudo acontece com os comandos grep / sed e awk diretamente no terminal em linha de comando
  • A consistência dos dados é resolvida por uma simples tarefa agendada

E aí estamos. Essa implementação levou 30 segundos para fazer e tem os mesmos resultados, pelo menos para mim, que seriam obtidos com um banco de dados relacional. Implementar uma solução MySQL teria tomado pelo menos 30 minutos ou mais, sem mencionar os problemas com injeção SQL, segurança, higienização de dados etc.

Fiquei feliz por não ter que fazer isso.

Advertências

Obviamente, em longo prazo, eu talvez tenha que armazenar os dados em uma tabela MySQL. Isso permitiria um gerenciamento muito melhor do sistema de armazenamento.

Esse #OpsHack funcionou para mim, porque meu conjunto de dados é simples. A quantidade de permutações possíveis dos meus dados é incrivelmente pequena. Assim que a complexidade dos dados aumenta, usar os logs de acesso para armazenar qualquer coisa passa a não ser mais uma opção.

Assim como o meu outro #OpsHack (usando zabbix para monitorar as apresentações HackerNews), esta foi a solução mais fácil para mim.

***

Mattias Geniar faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no https://ma.ttias.be/using-webserver-access-logs-as-a-database-storage-system/