GLPI редко взламывают через zero-day. Его взламывают через забытый установщик, webroot в неправильном корне и cookie сессии без флага Secure за прокси. Это руководство — чек-лист, который мы применяем при сопровождении клиентских сред: команды, virtual host и диагностические запросы, которые мы используем, чтобы закрыть поверхность атаки GLPI в продакшене.
Как GLPI компрометируют на самом деле
При сопровождении десятков сред картина повторяется: компрометация почти никогда не происходит из-за экзотической уязвимости ядра. Её причина — конфигурация. Четыре вектора, которые мы встречаем чаще всего, по порядку: (1) install/install.php, так и не удалённый после развёртывания; (2) DocumentRoot, указывающий на корень установки вместо public/, из-за чего files/_log/ и резервные копии конфигурации отдаются как текст; (3) всё ещё активные учётные данные по умолчанию (glpi/glpi); и (4) TLS, завершающийся на обратном прокси, при бэкенде на чистом HTTP и cookie сессии без Secure. Ни для одного из них не нужен эксплойт — достаточно curl. Начните аудит с проверки именно этого:
# 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
Если любой из этих запросов вернёт содержимое, остановите всё: среда открыта прямо сейчас, и исправление — наивысший приоритет.
1. Закройте поверхность: webroot в public/ и никаких остатков установки
- Направьте
DocumentRoot(илиrootв Nginx) на/var/www/glpi/public, а не на/var/www/glpi. Начиная с GLPI 10 webroot должна быть только папкаpublic/. - Удалите
install/install.phpпосле завершения установки или обновления. Обновление заново создаёт этот файл: типичная ошибка — обновить GLPI и забыть удалить его снова. - Убедитесь, что
config/,files/и любые.bakили.gitнаходятся вне досягаемости веб-сервера.
2. Virtual host: правильный корень, заголовки и PHP только через index.php
Стандарт GLPI 10 — маршрутизировать всё через public/index.php и не позволять напрямую исполнять никакой другой .php. Вот server block, который мы используем как основу в 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;
}
}
В Apache эквивалент — DocumentRoot /var/www/glpi/public с ограниченным блоком <Directory> и теми же заголовками через Header set. Совет из практики: включайте Strict-Transport-Security (HSTS) только после того, как сертификат полностью в порядке. С активным HSTS и сломанным TLS вы блокируете весь домен в браузерах пользователей на срок max-age.
3. Сессия и cookie: тихая утечка за прокси
Это находка, которую чаще всего упускают. Когда TLS завершается на прокси (Nginx/HAProxy), а GLPI работает по HTTP на бэкенде, cookie сессии по-прежнему должен быть помечен как Secure и HttpOnly. В Настройки > Общие, на вкладке безопасности, принудительно включите защищённые cookie и политику паролей (минимальная длина, сложность, срок действия). Без этого cookie сессии передаётся на внутреннем HTTP-участке без Secure, и достаточно перехватить трафик в этом сегменте, чтобы захватить сессию вошедшего специалиста. Также настройте время жизни сессии в соответствии с уровнем риска среды вместо длинного значения по умолчанию.
4. Учётные записи и аутентификация
Немедленно смените пароли glpi, tech, normal и post-only или отключите те, которые не используете. Выполните эту диагностику прямо в базе данных, чтобы выявить, что всё ещё зависит от локального пароля (authtype = 1) и является кандидатом на MFA/SSO или отключение:
-- Default GLPI accounts still active
SELECT name, is_active, last_login
FROM glpi_users
WHERE name IN ('glpi','tech','normal','post-only');
-- All accounts with an active LOCAL password (authtype = 1)
-- authtype: 1=local 2=mail 3=LDAP 4=external 5=CAS
SELECT id, name, is_active, last_login
FROM glpi_users
WHERE authtype = 1 AND is_active = 1
ORDER BY last_login;
Рекомендация для продакшена: SSO (Azure AD/Entra ID или другой провайдер) с MFA, а локальный вход ограничен одной аварийной учётной записью (break-glass) с надёжным паролем, хранящимся в хранилище секретов. В ядре GLPI нет встроенной MFA и блокировки по числу попыток: для MFA используйте подходящий плагин или сам SSO; против перебора паролей поставьте GLPI за fail2ban, читающий журнал доступа прокси. См. также наше руководство по SSO с Azure AD/Entra ID.
5. Права доступа: код только для чтения, запись только там, где нужно
Принцип прост: пользователь PHP-FPM (www-data) должен читать код, но никогда не записывать его. Единственное дерево с правом записи — GLPI_VAR_DIR. Так даже уязвимость в приложении не сможет закрепить вредоносный код в обслуживаемом каталоге.
| Каталог | Назначение | Владелец:группа | Запись для www-data |
|---|---|---|---|
| /var/www/glpi | Код + webroot public/ | root:www-data | Нет (750 / 640) |
| /etc/glpi | Конфигурация (config_db.php) | root:www-data | Нет (750 / 640) |
| /var/lib/glpi | GLPI_VAR_DIR (files, кэш, сессии) | www-data:www-data | Да (750) |
| /var/log/glpi | Журналы приложения | www-data:www-data | Да (750) |
Вынос config/ и files/ за пределы webroot (через GLPI_CONFIG_DIR и GLPI_VAR_DIR) — схема, которую мы применяем по умолчанию: если однажды webroot по ошибке окажется открыт, конфигурация и данные не утекут вместе с ним.
6. Обновления и непрерывный мониторинг
Поддерживайте версию в актуальном состоянии. Для большинства CVE GLPI патч уже существует к моменту, когда о них пишут в новостях; реальный риск — среда, застрявшая на три версии позади. Отслеживайте попытки аутентификации (всплески неудачных входов указывают на перебор паролей) в Zabbix/Grafana и следите за CVE установленной версии. Для этого мы используем модуль CVE Scan, который сопоставляет версию GLPI с известными CVE и проверяет ключевые точки среды. И не занимайтесь харденингом без плана восстановления: см. руководство по резервному копированию и аварийному восстановлению.
Типичные ошибки, которые мы видим при сопровождении
- Резервная копия конфигурации в webroot: кто-то сохраняет
config_db.php.bakперед редактированием; поскольку.bakне исполняется PHP, сервер отдаёт учётные данные базы данных открытым текстом. - Уцелевший устаревший alias: новый vhost указывает на
public/, но старыйAlias /glpiпродолжает открывать корень. - HSTS до сертификата: включение HSTS при незавершённой настройке TLS блокирует домен в браузерах пользователей.
- Локальный вход отключён без break-glass: локальный вход отключают, полностью полагаясь на SSO, а когда SSO падает, никто не может войти как администратор.
Сделать это один раз несложно; поддерживать это в десятках клиентских сред, где регулярные обновления заново создают install/, — это работа сопровождения. Именно этим и занимается NexTool: сопровождение GLPI от NexTool проверяет webroot, усиливает virtual host, пересматривает права доступа и запускает эту серию проверок в рамках цикла обслуживания.
Проверено командой NexTool Solutions.