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
- Aponte o
DocumentRoot(ourootno Nginx) para/var/www/glpi/public, nunca para/var/www/glpi. Desde o GLPI 10, apenas a pastapublic/deve ser a raiz web. - Remova
install/install.phpdepois 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. - Garanta que
config/,files/e qualquer.bakou.gitfiquem 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ório | Papel | Dono:grupo | Escrita www-data |
|---|---|---|---|
| /var/www/glpi | Código + raiz web public/ | root:www-data | Não (750 / 640) |
| /etc/glpi | Config (config_db.php) | root:www-data | Não (750 / 640) |
| /var/lib/glpi | GLPI_VAR_DIR (files, cache, sessões) | www-data:www-data | Sim (750) |
| /var/log/glpi | Registos da aplicação | www-data:www-data | Sim (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.bakantes de editar; como.baknã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 umAlias /glpiantigo 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.