chroot()
Apprenez comment la fonction PHP chroot() change le répertoire racine d'un processus pour créer un environnement de système de fichiers isolé.
La fonction PHP chroot()
La fonction chroot() change le répertoire racine du processus en cours d'exécution en le répertoire que vous lui fournissez, puis définit le répertoire de travail courant sur /. Après l'appel, le processus ne peut plus voir ni atteindre aucun fichier au-dessus de cette nouvelle racine — il est « emprisonné » à l'intérieur de l'arborescence. Cette technique est couramment appelée un chroot jail.
Cette page explique ce que fait chroot(), quand l'utiliser (et quand ne pas l'utiliser), sa syntaxe, et les limitations à connaître avant de s'y fier.
Syntaxe
chroot(string $directory): bool| Paramètre | Description |
|---|---|
$directory | Le chemin vers le répertoire qui devient la nouvelle racine (/) du processus. |
Valeur de retour : true en cas de succès, false en cas d'échec.
Prérequis
chroot() n'est pas disponible partout. Avant de l'utiliser, gardez ces contraintes à l'esprit :
- Elle fonctionne uniquement avec les SAPI CLI et CGI — elle n'est pas disponible avec la plupart des SAPI de module tels que
mod_phpou PHP-FPM gérant une requête web classique. - Elle n'est pas implémentée sur Windows.
- Le processus appelant doit disposer de privilèges root (super-utilisateur). Un utilisateur normal ne peut pas changer le répertoire racine.
En raison de ces exigences, chroot() est principalement utilisée dans les démons CLI PHP à longue durée de vie et les scripts de traitement en arrière-plan, et non dans le code qui gère des requêtes HTTP ordinaires.
Exemple de base
Ce script emprisonne le processus dans /var/www/jail, puis lit un chemin relatif à la nouvelle racine :
<?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";
}Après l'appel à chroot(), le chemin /data/config.txt fait en réalité référence à /var/www/jail/data/config.txt sur le système de fichiers réel. Le processus ne peut tout simplement pas exprimer un chemin qui sort de la prison.
Vérifier le répertoire de travail
Étant donné que chroot() déplace également le répertoire de travail vers /, vous pouvez confirmer le changement avec getcwd() :
<?php
chroot('/var/www/jail');
echo getcwd(); // "/" (which is /var/www/jail on the real filesystem)Si vous avez besoin d'un répertoire de travail différent à l'intérieur de la prison, définissez-le explicitement avec chdir() après l'appel à chroot().
Pourquoi utiliser chroot()
L'objectif de chroot() est l'isolation. Une fois qu'un processus est emprisonné :
- Il ne peut pas ouvrir, lire ou écrire des fichiers en dehors de la nouvelle racine, même avec des chemins absolus.
- Un bogue ou une faille qui tente une traversée de répertoire (
../../etc/passwd) n'a rien à traverser — il n'existe aucun chemin au-dessus de/. - Vous pouvez fournir une arborescence minimale (uniquement les fichiers dont le worker a réellement besoin), réduisant ainsi la surface d'attaque.
Un modèle courant consiste à démarrer un démon en tant que root, à appeler chroot() pour le confiner dans un bac à sable, puis à abandonner les privilèges avec posix_setuid() / posix_setgid() afin que le processus emprisonné ne s'exécute plus en tant que root.
chroot() vs open_basedir
Ces deux fonctions sont souvent confondues. Elles résolvent un problème similaire à des niveaux très différents :
chroot() | open_basedir | |
|---|---|---|
| Niveau | Racine de processus au niveau du système d'exploitation | Vérification de chemin par le moteur PHP |
| Où est défini | Dans le code à l'exécution | php.ini, .htaccess, pool FPM |
| Fonctionne avec les SAPI web | Non (CLI/CGI uniquement) | Oui |
| Nécessite des privilèges root | Oui | Non |
| Robustesse | Prison imposée par le système d'exploitation | Indicatif, peut être contourné par des liens symboliques |
Si vous avez seulement besoin de confiner une requête web classique dans un répertoire, open_basedir est l'outil pratique. Utilisez chroot() lorsque vous contrôlez un processus CLI et souhaitez une véritable frontière au niveau du système d'exploitation.
Limitations et pièges
- Ce n'est pas une frontière de sécurité parfaite. Un processus s'exécutant encore en tant que root à l'intérieur d'un chroot peut souvent s'en échapper. Abandonnez toujours les privilèges après l'emprisonnement.
- Dépendances manquantes. La prison ne contient pas
/lib,/etc,/usrà moins que vous ne les y ayez placés. Les fonctions qui dépendent de fichiers système (résolutions DNS, données de locale, fuseaux horaires, bibliothèques dynamiques) peuvent échouer à l'intérieur de la prison. - Irréversible pour le processus. Il n'existe pas de
unchroot(); le changement dure toute la vie du processus. - Dépendant de l'environnement. Étant donné que la disponibilité dépend de la SAPI et du système d'exploitation, vérifiez les appels et contrôlez la valeur de retour plutôt que de supposer le succès.
Conclusion
chroot() confine un processus PHP à une seule arborescence de répertoires en changeant sa racine vers ce répertoire et en réinitialisant le répertoire de travail sur /. C'est un puissant outil d'isolation au niveau du système d'exploitation pour les démons CLI privilégiés, mais il nécessite les privilèges root, est limité à CLI/CGI et n'est pas disponible sur Windows. Pour les restrictions par requête dans une pile web classique, préférez open_basedir, et considérez chroot() comme une couche dans une stratégie de défense en profondeur. Pour en savoir plus sur la manipulation des chemins et du système de fichiers, consultez chdir(), getcwd(), et le chapitre PHP Filesystem.
Diagramme
Voici comment chroot() redéfinit ce qu'un processus peut atteindre :
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 /];Remarque : la frontière est imposée par le système d'exploitation, elle s'applique donc à toutes les opérations de fichiers effectuées par le processus, et pas seulement aux appels de fonctions PHP.