Hardening de GLPI raramente cai por um zero-day. Cai pelo instalador esquecido, pelo webroot na raiz errada e pelo cookie de sessão sem a flag Secure atrás do proxy. Este guia é o checklist que aplicamos na sustentaçã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 sustentação de dezenas de ambientes, o padrão se repete: o comprometimento quase nunca vem de uma falha exótica do core. Vem de configuração. Os quatro vetores que mais encontramos, em ordem, são: (1) install/install.php nunca removido depois do deploy; (2) DocumentRoot apontando para a raiz da instalação em vez de public/, o que deixa files/_log/ e backups de config servíveis como texto; (3) credenciais padrão (glpi/glpi) ainda ativas; e (4) TLS terminando no proxy reverso com o backend em HTTP puro e o cookie de sessão sem Secure. Nenhum desses precisa de exploit, só de um curl. Comece a auditoria testando exatamente isso:
# Instalador ainda no ar? (esperado: 404)
curl -s -o /dev/null -w "%{http_code}\n" https://glpi.cliente.com.br/install/install.php
# Log de erros exposto? (esperado: 403/404, NUNCA 200 com conteúdo)
curl -s https://glpi.cliente.com.br/files/_log/php-errors.log | head
# Backup de config vazando credencial do banco? (esperado: nada)
curl -s https://glpi.cliente.com.br/config/config_db.php.bak | head
Se qualquer um desses devolver conteúdo, pare tudo: o ambiente está exposto agora, e a correção é prioridade zero.
1. Feche a superfície: webroot 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 o webroot. - Remova
install/install.phpapós concluir a instalação ou o upgrade. O upgrade recria esse arquivo: 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 executando PHP
O padrão do GLPI 10 é rotear 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.br;
root /var/www/glpi/public; # nunca /var/www/glpi
index index.php;
ssl_certificate /etc/letsencrypt/live/glpi.cliente.com.br/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/glpi.cliente.com.br/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Cabeçalhos de segurança
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 roteia 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ó habilite Strict-Transport-Security (HSTS) depois que o certificado estiver 100%. Com HSTS ativo e um TLS quebrado você bloqueia o domínio inteiro no navegador dos usuários pelo tempo do max-age.
3. Sessão e cookies: o vazamento silencioso atrás do proxy
Este é o achado que mais passa despercebido. Quando o TLS termina no proxy (Nginx/HAProxy) e o GLPI roda em HTTP no backend, o cookie de sessão precisa continuar marcado como Secure e HttpOnly. Em Configuração > Geral, na aba de segurança, force cookies seguros e uma política de senha (tamanho mínimo, complexidade, expiração). Sem isso, o cookie de sessão trafega no salto interno HTTP sem Secure e basta capturar o tráfego nesse segmento para sequestrar a sessão de um técnico logado. Ajuste também o tempo de vida da sessão para algo compatível com o risco do ambiente, em vez do padrão longo.
4. Contas e autenticação
Troque imediatamente as senhas de glpi, tech, normal e post-only, ou desative as que não usa. Rode este diagnóstico direto no banco para mapear o que ainda depende de senha local (authtype = 1) e é candidato a MFA/SSO ou desativação:
-- Contas padrão 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 senha 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 provedor) com MFA, e login local restrito a uma única conta de emergência (break-glass) com senha forte guardada em cofre. O core do GLPI não tem MFA nativo nem bloqueio por tentativas: para MFA use um plugin apropriado ou o próprio SSO; para brute force, coloque o GLPI atrás de fail2ban lendo o log de acesso do proxy. Veja também nosso guia de SSO com Azure AD/Entra ID.
5. Permissões: código só-leitura, escrita só no que precisa
O princípio é simples: o usuário 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 + webroot 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 | Logs da aplicação | www-data:www-data | Sim (750) |
Separar config/ e files/ para fora do webroot (via GLPI_CONFIG_DIR e GLPI_VAR_DIR) é o layout que adotamos por padrão: se um dia o webroot for exposto por engano, config e dados não vão junto.
6. Atualizações e monitoramento contínuo
Mantenha a versão em dia. A maioria dos CVEs do GLPI já tem patch quando vira notícia; o risco real é o ambiente parado três versões atrás. Monitore tentativas de autenticação (picos de falha de login sugerem brute force) no Zabbix/Grafana e acompanhe as CVEs da versão instalada. Para isso usamos o módulo CVE Scan, que cruza a versão do GLPI com as CVEs conhecidas e audita pontos do ambiente. E não trate hardening sem plano de recuperação: veja o guia de backup e disaster recovery.
Erros comuns que vemos na sustentação
- Backup de config no webroot: alguém salva
config_db.php.bakantes de editar; como.baknão é executado pelo PHP, o servidor entrega a credencial do banco em texto puro. - Alias legado sobrevivendo: o vhost novo aponta para
public/, mas umAlias /glpiantigo continua expondo a raiz. - HSTS antes do certificado: ativar HSTS com TLS incompleto tranca o domínio no navegador dos usuários.
- Login local desativado sem break-glass: cortam o login local confiando 100% no SSO e, quando o SSO cai, ninguém entra como admin.
Fazer isso uma vez é fácil; manter em dezenas de ambientes de clientes, com upgrades recorrentes recriando o install/, é trabalho de sustentação. É exatamente o que a NexTool faz: a sustentação de GLPI da NexTool valida o webroot, endurece o virtual host, revisa permissões e roda essa 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.