Der public-Ordner im GLPI: sichere Konfiguration für die Produktion

Warum das DocumentRoot von GLPI 10 auf den Ordner public zeigen muss: was offenliegt, wenn es das nicht tut, gehärteter Virtual Host für Apache und Nginx, ein Prüfskript und die Produktions-Checkliste aus dem Betriebssupport von NexTool.

In diesem Artikel6 Abschnitte

Wenn das DocumentRoot Ihres GLPI auf das Stammverzeichnis der Installation zeigt und nicht auf den Ordner public, ist die Datei mit dem Passwort Ihrer Datenbank nur einen GET entfernt. Seit GLPI 10 ist das die wichtigste Sicherheitsentscheidung beim Deployment – und diejenige, die wir in Umgebungen, die zum Betriebssupport zu uns kommen, am häufigsten falsch vorfinden, gerade weil das System auch falsch konfiguriert einwandfrei funktioniert.

Warum das falsche Webroot unbemerkt bleibt

GLPI 10 öffnet und arbeitet weiterhin normal, wenn Sie den Webserver auf das Stammverzeichnis (/var/www/glpi) statt auf /var/www/glpi/public zeigen lassen. Auf dem Bildschirm bricht nichts, niemand erstellt ein Ticket, und deshalb überlebt der Fehler jahrelang. Im Betriebssupport von GLPI-Umgebungen unserer Kunden ist das der häufigste stille Befund beim Onboarding: Die Umgebung reagiert gut, aber https://glpi.cliente.com/files/_log/php-errors.log liefert das Anwendungslog als gewöhnlichen Download aus.

Das Detail, das sich erst im Betrieb zeigt: Unter Apache wurde die alte Installation teilweise durch die .htaccess-Dateien gerettet, die in config/, files/ und ähnlichen Verzeichnissen verteilt sind und den Zugriff verweigerten. Das vermittelt ein falsches Gefühl von Schutz. Unter Nginx gibt es diese .htaccess schlicht nicht – Nginx liest sie nie. Ein Wechsel von Apache zu Nginx mit dem falschen root macht also aus einer teilweisen Offenlegung eine vollständige: files/, config/ und der gesamte Quellcode werden zu statischem Inhalt, der jedem ausgeliefert wird. Der Wechsel des Webservers ist der klassische Moment, in dem diese Bombe hochgeht.

Was offenliegt, wenn das DocumentRoot falsch ist

Der Ordner public existiert, um nur den Router (index.php), die .htaccess und die statischen Dateien (CSS, JS, Bilder) zu veröffentlichen. Alles andere muss für den Browser unerreichbar sein. Die folgende Tabelle ist die Prüfmatrix, die wir verwenden: Mit public als Webroot müssen alle unten aufgeführten URLs 404 zurückgeben.

PfadInhaltAuswirkung bei OffenlegungErwarteter Status
/config/config_db.phpHost, Benutzer und Passwort der DatenbankVollständiges Datenleck der Datenbank404
/files/_log/php-errors.log, sql-errors.logAufklärung der Anwendung und Abfluss personenbezogener Daten404
/files/_dumps/Vom GLPI erzeugte SQL-DumpsHerunterladbare Kopie der Datenbank404
/files/_sessions/Aktive SitzungsdateienSession-Hijacking und Diebstahl des CSRF-Tokens404
/src/PHP-Quellcode der AnwendungLandkarte der Anwendung für Angriffe404
/vendor/Composer-AbhängigkeitenAusnutzung von CVEs in Bibliotheken404
/install/install.phpInstallationsassistentNeukonfiguration und Übernahme der Umgebung404 (nach der Installation entfernen)

Vorsicht vor der falschen „Sicherheit“: Bei aktivem PHP kann /config/config_db.php mit 200 und leerem Inhalt antworten (die Datei definiert nur eine Klasse und gibt nichts aus). Ein leeres 200 bedeutet nicht, dass alles in Ordnung ist – es bedeutet, dass die Datei innerhalb des Webroots liegt. Was tatsächlich als Download abfließt, sind die Nicht-PHP-Dateien: files/_log/*.log, files/_dumps/*.sql und jedes vergessene Backup im Verzeichnisbaum. Deshalb ist der Maßstab 404, nicht „leere Seite“.

Apache: gehärteter Virtual Host

Das DocumentRoot zeigt auf public, das Verzeichnislisting ist deaktiviert, und reines HTTP wird auf HTTPS umgeleitet. FallbackResource ersetzt das Routing der .htaccess, ohne AllowOverride All zu erfordern:

# /etc/apache2/sites-available/glpi.conf
<VirtualHost *:80>
    ServerName glpi.suaempresa.com
    # Nada de GLPI em HTTP puro
    Redirect permanent / https://glpi.suaempresa.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName glpi.suaempresa.com
    DocumentRoot /var/www/glpi/public

    <Directory /var/www/glpi/public>
        Require all granted
        Options -Indexes +FollowSymLinks
        # Roteia sem depender de AllowOverride All
        FallbackResource /index.php
    </Directory>

    # Nega qualquer arquivo oculto (.git, .env, .htaccess)
    <FilesMatch "^\.">
        Require all denied
    </FilesMatch>

    # Cabeçalhos de segurança
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    SSLEngine On
    SSLCertificateFile /etc/ssl/certs/glpi.crt
    SSLCertificateKeyFile /etc/ssl/private/glpi.key
</VirtualHost>

Nginx: ohne das Sicherheitsnetz der .htaccess

Unter Nginx gibt es keine .htaccess, die Sie rettet, daher muss root von Anfang an public sein. Achten Sie auf zwei Zeilen, die fast immer fehlen: das try_files $uri =404; im PHP-Block und die explizite Sperre versteckter Dateien.

# /etc/nginx/sites-available/glpi.conf
server {
    listen 80;
    server_name glpi.suaempresa.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name glpi.suaempresa.com;
    root /var/www/glpi/public;
    index index.php;

    ssl_certificate     /etc/ssl/certs/glpi.crt;
    ssl_certificate_key /etc/ssl/private/glpi.key;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;

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

    location ~ \.php$ {
        # =404 impede execução via PATH_INFO forjado
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Nginx não lê .htaccess: negue arquivos ocultos na mão
    location ~ /\. {
        deny all;
    }
}

Der häufige Fehler hier ist, einen generischen PHP-Block aus dem Internet zu kopieren, ohne das try_files $uri =404;. Ohne diese Zeile kann eine Anfrage wie /uploads/foto.jpg/x.php über PATH_INFO an FPM weitergereicht und ausgeführt werden, was den Weg zu RCE öffnet, wenn der Angreifer eine Datei in den Verzeichnisbaum schreiben kann. Mit dem =404 lehnt Nginx die Anfrage ab, bevor sie PHP erreicht.

Prüfung nach dem Deployment: der 30-Sekunden-Test

Führen Sie nach dem Neuladen des Webservers dieses Skript gegen die echte Domain aus. Jede Zeile muss OK melden. Jedes FALHA ist ein ausgelieferter Pfad, der nicht existieren dürfte:

#!/usr/bin/env bash
BASE="https://glpi.suaempresa.com"

# Todas as URLs abaixo DEVEM retornar 404 num webroot correto
for path in \
  /config/config_db.php \
  /src/ \
  /vendor/ \
  /files/_log/php-errors.log \
  /install/install.php ; do
  code=$(curl -sk -o /dev/null -w '%{http_code}' "${BASE}${path}")
  if [ "$code" = "404" ]; then
    echo "OK    ${path} -> ${code}"
  else
    echo "FALHA ${path} -> ${code} (EXPOSTO)"
  fi
done

Im Betriebssupport haben wir dieses Skript fest in die Onboarding-Checkliste jedes neuen Kunden aufgenommen und führen es bei jedem Wechsel des Webservers, jedem Upgrade und jeder Änderung am Reverse Proxy erneut aus – denn ein korrektes Webroot ist kein Dauerzustand, sondern etwas, das eine unachtsame Migration in Sekunden rückgängig macht.

Produktions-Checkliste

  1. DocumentRoot / root zeigt auf .../public, niemals auf das Stammverzeichnis der Installation.
  2. Entfernen Sie install/install.php, sobald die Installation bzw. das Update abgeschlossen ist (das GLPI selbst zeigt eine rote Warnung, solange die Datei existiert).
  3. HTTPS erzwingen mit 301-Weiterleitung von Port 80 und aktiviertem Strict-Transport-Security.
  4. Optionale Härtung: config/ und files/ über GLPI_CONFIG_DIR und GLPI_VAR_DIR aus dem Web-Verzeichnisbaum verschieben, sodass im Webroot nur public bleibt.
  5. Das Prüfskript ausführen und für alle Pfade 404 verlangen.
  6. Nach dem Start das Sicherheits-Panel des GLPI selbst (Einrichten) prüfen – es meldet offene Punkte wie eine noch vorhandene install.php.

Das Webroot anzupassen dauert zehn Minuten; herauszufinden, dass das Datenbankpasswort drei Jahre lang herunterladbar war, kostet sehr viel mehr. Wenn Sie ein neues GLPI aufsetzen, von Apache zu Nginx migrieren oder eine Umgebung ohne Konfigurationshistorie übernehmen, prüft das GLPI-Betriebssupport-Team von NexTool das Webroot, härtet den Virtual Host und führt die Prüfbatterie als Teil des Onboardings aus. Sprechen Sie mit uns über GLPI-Betriebssupport.


Geprüft vom Team der NexTool Solutions.

Häufig gestellte Fragen

Weil das Routing von GLPI 10 nicht vom Webroot abhängt, um die Bildschirme zu laden: Es löst die Pfade intern auf. Die Anwendung öffnet und arbeitet normal mit dem DocumentRoot im Stammverzeichnis der Installation, sodass optisch nichts bricht. Das Problem ist still: config/, files/ und der Quellcode werden für den Browser erreichbar. Da es kein Symptom gibt, überlebt der Fehler jahrelang, bis jemand die richtigen URLs testet.

Nein. Die .htaccess ist eine reine Apache-Funktion. Nginx ignoriert diese Dateien vollständig, auch die, die das GLPI in config/ und files/ ablegt, um den Zugriff zu verweigern. Deshalb ist eine Migration von Apache zu Nginx, die das falsche root übernimmt, gefährlich: Der teilweise Schutz durch Apache verschwindet, und die Offenlegung wird vollständig. Unter Nginx ist es Pflicht, nicht optional, root auf public zeigen zu lassen.

Ja. Solange die install.php existiert, zeigt das GLPI selbst eine rote Sicherheitswarnung, und die Datei erlaubt eine Neukonfiguration der Umgebung. Entfernen (rm public/install/install.php) oder benennen Sie sie direkt nach der Installation oder dem Update um. Zur Erinnerung: Wenn das Webroot korrekt auf public zeigt, liefert der Pfad /install/install.php bereits 404 – das Entfernen der Datei ist aber die empfohlene gestaffelte Verteidigung.

Ja, und das ist die empfohlene Härtung für sensible Umgebungen. Das GLPI unterstützt die Konstanten GLPI_CONFIG_DIR und GLPI_VAR_DIR, um config/ und files/ auf Verzeichnisse außerhalb des Web-Verzeichnisbaums zu legen (zum Beispiel /etc/glpi und /var/lib/glpi). So liegen selbst dann, wenn künftig jemand das Webroot falsch setzt, weder Zugangsdaten noch Logs im vom Webserver ausgelieferten Pfad.

Führen Sie curl gegen URLs aus, die in einem korrekten Webroot nie existieren dürften, etwa /config/config_db.php, /vendor/ und /files/_log/php-errors.log. Alle müssen 404 zurückgeben. Achten Sie auf das falsch Positive: config_db.php kann mit 200 und leerem Inhalt antworten, wenn PHP sie ausführt – das ist bereits ein Zeichen für ein falsches Webroot. Der Maßstab ist 404, nicht eine leere Seite.

Brauchen Sie Hilfe?