chroot()
Erfahren Sie, wie PHP's chroot() das Root-Verzeichnis eines Prozesses ändert, um eine isolierte Dateisystem-Umgebung zu schaffen – mit Syntax und Beispielen.
PHP chroot() Funktion
Die Funktion chroot() ändert das Root-Verzeichnis des aktuell laufenden Prozesses auf das angegebene Verzeichnis und setzt das aktuelle Arbeitsverzeichnis anschließend auf /. Nach dem Aufruf kann der Prozess keine Datei mehr sehen oder erreichen, die sich oberhalb dieses neuen Root befindet — er ist innerhalb des Verzeichnisbaums "eingesperrt". Diese Technik wird allgemein als chroot-Jail bezeichnet.
Diese Seite erklärt, was chroot() tut, wann man es verwenden sollte (und wann nicht), seine Syntax sowie die Einschränkungen, die man kennen muss, bevor man sich darauf verlässt.
Syntax
chroot(string $directory): bool| Parameter | Beschreibung |
|---|---|
$directory | Der Pfad zu dem Verzeichnis, das zum neuen Root (/) des Prozesses wird. |
Rückgabewert: true bei Erfolg, false bei Fehler.
Voraussetzungen
chroot() ist nicht überall verfügbar. Beachten Sie vor der Verwendung folgende Einschränkungen:
- Es funktioniert nur unter CLI- und CGI-SAPIs — es ist nicht unter den meisten Modul-SAPIs wie
mod_phpoder PHP-FPM verfügbar, wenn diese eine typische Web-Anfrage verarbeiten. - Es ist nicht unter Windows implementiert.
- Der aufrufende Prozess muss über Root-Rechte (Superuser-Rechte) verfügen. Ein normaler Benutzer kann das Root-Verzeichnis nicht ändern.
Aufgrund dieser Voraussetzungen wird chroot() hauptsächlich in langlebigen PHP-CLI-Daemons und Worker-Skripten verwendet, nicht in Code, der gewöhnliche HTTP-Anfragen verarbeitet.
Einfaches Beispiel
Dieses Skript sperrt den Prozess in /var/www/jail ein und liest anschließend einen Pfad relativ zum neuen Root:
<?php
// Must be run as root, on CLI.
if (chroot('/var/www/jail')) {
echo "Root directory changed.\n";
// Paths are now relative to /var/www/jail.
// What was /var/www/jail/data/config.txt is now /data/config.txt
$contents = file_get_contents('/data/config.txt');
echo $contents;
} else {
echo "Failed to change root directory.\n";
}Nach dem Aufruf von chroot() verweist der Pfad /data/config.txt tatsächlich auf /var/www/jail/data/config.txt im echten Dateisystem. Der Prozess kann schlicht keinen Pfad ausdrücken, der aus dem Jail ausbricht.
Das Arbeitsverzeichnis bestätigen
Da chroot() das Arbeitsverzeichnis ebenfalls auf / setzt, können Sie die Änderung mit getcwd() bestätigen:
<?php
chroot('/var/www/jail');
echo getcwd(); // "/" (which is /var/www/jail on the real filesystem)Falls Sie ein anderes Arbeitsverzeichnis innerhalb des Jails benötigen, setzen Sie es nach dem chroot()-Aufruf explizit mit chdir().
Warum chroot() verwenden
Das Ziel von chroot() ist Isolation. Sobald ein Prozess eingesperrt ist:
- Kann er keine Dateien außerhalb des neuen Root öffnen, lesen oder schreiben, selbst nicht mit absoluten Pfaden.
- Hat ein Fehler oder Exploit, der Directory-Traversal (
../../etc/passwd) versucht, nichts, das er durchqueren kann — es gibt keinen Pfad oberhalb von/. - Können Sie einen minimalen Verzeichnisbaum bereitstellen (nur die Dateien, die der Worker tatsächlich benötigt), was die Angriffsfläche verringert.
Ein gängiges Muster besteht darin, einen Daemon als Root zu starten, chroot() aufzurufen, um ihn in eine Sandbox einzusperren, und dann mit posix_setuid() / posix_setgid() Privilegien abzugeben, sodass der eingesperrte Prozess nicht mehr als Root läuft.
chroot() vs. open_basedir
Diese werden häufig verwechselt. Sie lösen ein ähnliches Problem auf sehr unterschiedlichen Ebenen:
chroot() | open_basedir | |
|---|---|---|
| Ebene | Root des Betriebssystem-Prozesses | Pfadprüfung der PHP-Engine |
| Wo gesetzt | Im Code zur Laufzeit | php.ini, .htaccess, FPM-Pool |
| Funktioniert unter Web-SAPIs | Nein (nur CLI/CGI) | Ja |
| Root-Rechte erforderlich | Ja | Nein |
| Stärke | Vom Betriebssystem erzwungenes Jail | Empfehlend, kann durch Symlinks geschwächt werden |
Wenn Sie nur eine normale Web-Anfrage in einem Verzeichnis halten möchten, ist open_basedir das praktische Werkzeug. Verwenden Sie chroot(), wenn Sie einen CLI-Prozess kontrollieren und eine echte Grenze auf Betriebssystemebene wünschen.
Einschränkungen und Fallstricke
- Keine perfekte Sicherheitsgrenze. Ein Prozess, der im Chroot noch als Root läuft, kann häufig ausbrechen. Geben Sie Privilegien immer nach dem Einsperren ab.
- Fehlende Abhängigkeiten. Das Jail besitzt kein
/lib,/etc,/usr, es sei denn, Sie legen sie dort ab. Funktionen, die auf Systemdateien angewiesen sind (DNS-Abfragen, Locale-Daten, Zeitzonen, dynamische Bibliotheken), können innerhalb des Jails fehlschlagen. - Einmalig für den Prozess. Es gibt kein
unchroot(); die Änderung gilt für die gesamte Lebensdauer des Prozesses. - Umgebungsabhängig. Da die Verfügbarkeit von SAPI und Betriebssystem abhängt, sollten Aufrufe abgesichert und der Rückgabewert überprüft werden, anstatt Erfolg vorauszusetzen.
Fazit
chroot() begrenzt einen PHP-Prozess auf einen einzelnen Verzeichnisbaum, indem es dessen Root auf dieses Verzeichnis ändert und das Arbeitsverzeichnis auf / zurücksetzt. Es ist ein leistungsstarkes Isolationswerkzeug auf Betriebssystemebene für privilegierte CLI-Daemons, erfordert jedoch Root-Rechte, ist auf CLI/CGI beschränkt und unter Windows nicht verfügbar. Für Einschränkungen pro Anfrage in einem normalen Web-Stack greifen Sie stattdessen auf open_basedir zurück und betrachten chroot() als eine Schicht einer Defense-in-Depth-Strategie. Weitere Informationen zur Arbeit mit Pfaden und dem Dateisystem finden Sie unter chdir(), getcwd() und im Kapitel PHP Filesystem.
Diagramm
So verändert chroot(), was ein Prozess erreichen kann:
graph TD;
A[PHP Process] --> B{chroot('/var/www/jail')};
B --> C[New root = /var/www/jail];
C --> D[Working dir set to /];
D -->|Path /data/config.txt| E[Allowed: inside jail];
D -->|Path ../../etc/passwd| F[Blocked: nothing above /];Hinweis: Die Grenze wird vom Betriebssystem durchgesetzt und gilt daher für jeden Datei-Vorgang des Prozesses, nicht nur für PHP-Funktionsaufrufe.