Segurança do GLPI: Hardening e Boas Práticas

Checklist prática de hardening do GLPI em produção: raiz web em public/, virtual host Nginx, cabeçalhos de segurança, cookie de sessão Secure por trás do proxy, permissões, contas e diagnóstico SQL - diretamente da manutenção de ambientes NexTool.

O hardening do GLPI raramente cai por um zero-day. Cai pelo instalador esquecido, pela raiz web apontada ao diretório errado e pelo cookie de sessão sem a marca Secure por trás do proxy. Este guia é a checklist que aplicamos na manutenção de ambientes de clientes: os comandos, o virtual host e as consultas de diagnóstico que usamos para fechar a superfície de ataque de um GLPI em produção.

Por onde um GLPI é comprometido de verdade

Na manutenção de dezenas de ambientes, o padrão repete-se: o comprometimento quase nunca vem de uma falha exótica do core. Vem da configuração. Os quatro vetores que mais encontramos, por ordem, são: (1) install/install.php nunca removido depois do deploy; (2) o DocumentRoot a apontar para a raiz da instalação em vez de public/, o que deixa files/_log/ e as cópias de segurança de configuração servíveis como texto; (3) credenciais por omissão (glpi/glpi) ainda ativas; e (4) o TLS a terminar no proxy inverso com o backend em HTTP simples e o cookie de sessão sem Secure. Nenhum precisa de exploit, apenas de um curl. Comece a auditoria a testar exatamente isto:

# Instalador ainda no ar? (esperado: 404)
curl -s -o /dev/null -w "%{http_code}\n" https://glpi.cliente.com/install/install.php

# Registo de erros exposto? (esperado: 403/404, NUNCA 200 com conteúdo)
curl -s https://glpi.cliente.com/files/_log/php-errors.log | head

# Cópia de config a expor a credencial da base de dados? (esperado: nada)
curl -s https://glpi.cliente.com/config/config_db.php.bak | head

Se algum deles devolver conteúdo, pare tudo: o ambiente está exposto agora, e a correção é prioridade zero.

1. Feche a superfície: raiz web em public/ e sem sobras de instalação

  1. Aponte o DocumentRoot (ou root no Nginx) para /var/www/glpi/public, nunca para /var/www/glpi. Desde o GLPI 10, apenas a pasta public/ deve ser a raiz web.
  2. Remova install/install.php depois de concluir a instalação ou a atualização. A atualização recria esse ficheiro: o erro comum é atualizar o GLPI e esquecer de removê-lo de novo.
  3. Garanta que config/, files/ e qualquer .bak ou .git fiquem fora do alcance do servidor web.

2. Virtual host: raiz correta, headers e só o index.php a executar PHP

O padrão do GLPI 10 é encaminhar tudo por public/index.php e não deixar nenhum outro .php ser executado diretamente. Este é o server block que usamos como base no Nginx:

# /etc/nginx/sites-available/glpi.conf
server {
    listen 443 ssl;
    http2 on;
    server_name glpi.cliente.com;

    root /var/www/glpi/public;   # nunca /var/www/glpi
    index index.php;

    ssl_certificate     /etc/letsencrypt/live/glpi.cliente.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/glpi.cliente.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Cabecalhos de seguranca
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    # Somente o index.php encaminha PHP
    location ~ ^/index\.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    # Qualquer outro .php: negar
    location ~ \.php$ {
        return 404;
    }
}

No Apache, o equivalente é DocumentRoot /var/www/glpi/public com o bloco <Directory> restrito e os mesmos cabeçalhos via Header set. Um cuidado de campo: só ative Strict-Transport-Security (HSTS) depois de o certificado estar 100%. Com HSTS ativo e um TLS partido, bloqueia o domínio inteiro no navegador dos utilizadores durante todo o max-age.

3. Sessão e cookies: a fuga silenciosa por trás do proxy

Este é o achado que mais passa despercebido. Quando o TLS termina no proxy (Nginx/HAProxy) e o GLPI corre em HTTP no backend, o cookie de sessão tem de continuar marcado como Secure e HttpOnly. Em Configuração > Geral, no separador de segurança, force cookies seguros e uma política de palavra-passe (comprimento mínimo, complexidade, expiração). Sem isso, o cookie de sessão circula no salto interno HTTP sem Secure, e basta capturar o tráfego nesse segmento para sequestrar a sessão de um técnico autenticado. Ajuste também o tempo de vida da sessão para algo compatível com o risco do ambiente, em vez do longo valor por omissão.

4. Contas e autenticação

Mude imediatamente as palavras-passe de glpi, tech, normal e post-only, ou desative as que não usa. Corra este diagnóstico diretamente na base de dados para mapear o que ainda depende de palavra-passe local (authtype = 1) e é candidato a MFA/SSO ou desativação:

-- Contas por omissao do GLPI ainda ativas
SELECT name, is_active, last_login
FROM   glpi_users
WHERE  name IN ('glpi','tech','normal','post-only');

-- Todas as contas com palavra-passe LOCAL ativa (authtype = 1)
-- authtype: 1=local  2=mail  3=LDAP  4=externa  5=CAS
SELECT id, name, is_active, last_login
FROM   glpi_users
WHERE  authtype = 1 AND is_active = 1
ORDER  BY last_login;

A recomendação para produção: SSO (Azure AD/Entra ID ou outro fornecedor) com MFA, e login local restrito a uma única conta de emergência (break-glass) com palavra-passe forte guardada num cofre. O core do GLPI não tem MFA nativo nem bloqueio por tentativas: para MFA use um plugin adequado ou o próprio SSO; para força bruta, coloque o GLPI atrás do fail2ban a ler o registo de acesso do proxy. Veja também o nosso guia de SSO com Azure AD/Entra ID.

5. Permissões: código só de leitura, escrita só onde é preciso

O princípio é simples: o utilizador do PHP-FPM (www-data) deve ler o código, mas nunca escrevê-lo. A única árvore com escrita é o GLPI_VAR_DIR. Assim, mesmo uma falha na aplicação não consegue persistir código malicioso no diretório servido.

DiretórioPapelDono:grupoEscrita www-data
/var/www/glpiCódigo + raiz web public/root:www-dataNão (750 / 640)
/etc/glpiConfig (config_db.php)root:www-dataNão (750 / 640)
/var/lib/glpiGLPI_VAR_DIR (files, cache, sessões)www-data:www-dataSim (750)
/var/log/glpiRegistos da aplicaçãowww-data:www-dataSim (750)

Separar config/ e files/ para fora da raiz web (via GLPI_CONFIG_DIR e GLPI_VAR_DIR) é o layout que adotamos por omissão: se um dia a raiz web for exposta por engano, a config e os dados não vão junto.

6. Atualizações e monitorização contínua

Mantenha a versão em dia. A maioria dos CVE do GLPI já tem patch quando vira notícia; o risco real é o ambiente parado três versões atrás. Monitorize as tentativas de autenticação (picos de falha de login sugerem força bruta) no Zabbix/Grafana e acompanhe os CVE da versão instalada. Para isso usamos o módulo CVE Scan, que cruza a versão do GLPI com os CVE conhecidos e audita pontos do ambiente. E não trate o hardening sem plano de recuperação: veja o guia de backup e disaster recovery.

Erros comuns que vemos na manutenção

  • Cópia de config na raiz web: alguém guarda config_db.php.bak antes de editar; como .bak não é executado pelo PHP, o servidor entrega a credencial da base de dados em texto simples.
  • Alias legado a sobreviver: o vhost novo aponta para public/, mas um Alias /glpi antigo continua a expor a raiz.
  • HSTS antes do certificado: ativar HSTS com um TLS incompleto tranca o domínio no navegador dos utilizadores.
  • Login local desativado sem break-glass: cortam o login local a confiar 100% no SSO e, quando o SSO cai, ninguém entra como admin.

Fazer isto uma vez é fácil; mantê-lo em dezenas de ambientes de clientes, com atualizações recorrentes a recriar o install/, é trabalho de manutenção. É exatamente o que a NexTool faz: a manutenção de GLPI da NexTool valida a raiz web, endurece o virtual host, revê permissões e corre esta bateria de verificação como parte do ciclo de manutenção.


Este conteúdo foi produzido com auxílio de inteligência artificial e revisado pela equipe Nextool Solutions.

Perguntas Frequentes

Faça um curl para /install/install.php, /files/_log/php-errors.log e /config/config_db.php.bak. A resposta esperada é 403 ou 404; qualquer 200 com conteúdo significa exposição. A causa é quase sempre o DocumentRoot a apontar para a raiz da instalação em vez de public/.

Funciona, mas é inseguro. Com a raiz errada, ficheiros como files/_log/ e as cópias de config tornam-se texto servível pelo servidor web. Desde o GLPI 10, public/ é a única raiz web correta porque isola o index.php do resto da instalação.

Não no core. Coloque o GLPI atrás do fail2ban a ler o registo de acesso do proxy inverso, ou use SSO/MFA para reduzir a superfície. Em paralelo, monitorize os picos de falha de login no Zabbix ou Grafana, que são o sinal típico de um ataque de força bruta.

Em Configuração > Geral, separador de segurança, ative os cookies seguros; garanta que o proxy encaminha o X-Forwarded-Proto e que o GLPI reconhece que está sob HTTPS. Sem a marca Secure, o cookie de sessão circula no salto HTTP interno e pode ser capturado nesse segmento.

Não nativamente no core. Use um plugin de MFA (TOTP) ou SSO com Azure AD/Entra ID ou Okta, que já trazem MFA. Mantenha sempre uma conta local de emergência (break-glass) com palavra-passe forte, para não perder o acesso administrativo se o SSO cair.

Precisa de ajuda?