chroot()
Scopri come la funzione chroot() di PHP cambia la directory radice del processo, con sintassi, esempi e limitazioni.
Funzione PHP chroot()
La funzione chroot() cambia la directory radice del processo in esecuzione nella directory specificata, portando poi la directory di lavoro corrente a /. Dopo la chiamata, il processo non può più vedere né raggiungere alcun file al di sopra della nuova radice — è "rinchiuso" all'interno di quell'albero di directory. Questa tecnica è comunemente chiamata chroot jail.
Questa pagina spiega cosa fa chroot(), quando utilizzarla (e quando non farlo), la sua sintassi e le limitazioni da conoscere prima di affidarsi ad essa.
Sintassi
chroot(string $directory): bool| Parametro | Descrizione |
|---|---|
$directory | Il percorso della directory che diventa la nuova radice (/) del processo. |
Valore restituito: true in caso di successo, false in caso di errore.
Requisiti
chroot() non è disponibile ovunque. Prima di utilizzarla, tieni presenti questi vincoli:
- Funziona solo con CLI e CGI SAPI — non è disponibile per la maggior parte dei module SAPI come
mod_phpo PHP-FPM durante una normale richiesta web. - Non è implementata su Windows.
- Il processo chiamante deve avere privilegi di root (superutente). Un utente normale non può cambiare la directory radice.
A causa di questi requisiti, chroot() viene utilizzata principalmente in daemon CLI PHP a lunga esecuzione e script worker, non nel codice che gestisce le normali richieste HTTP.
Esempio Base
Questo script rinchiude il processo in /var/www/jail, poi legge un percorso relativo alla nuova radice:
<?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";
}Dopo la chiamata a chroot(), il percorso /data/config.txt fa riferimento in realtà a /var/www/jail/data/config.txt sul filesystem reale. Il processo semplicemente non può costruire un percorso che esca dalla jail.
Confermare la Directory di Lavoro
Poiché chroot() sposta anche la directory di lavoro a /, puoi confermare il cambiamento con getcwd():
<?php
chroot('/var/www/jail');
echo getcwd(); // "/" (which is /var/www/jail on the real filesystem)Se hai bisogno di una directory di lavoro diversa all'interno della jail, impostala esplicitamente con chdir() dopo la chiamata a chroot().
Perché Usare chroot()
Lo scopo di chroot() è l'isolamento. Una volta che un processo è nella jail:
- Non può aprire, leggere o scrivere file al di fuori della nuova radice, nemmeno con percorsi assoluti.
- Un bug o un exploit che tenta il directory traversal (
../../etc/passwd) non ha nulla da attraversare — non esiste alcun percorso sopra/. - Puoi fornire un albero di directory minimale (solo i file di cui il worker ha effettivamente bisogno), riducendo la superficie di attacco.
Un pattern comune consiste nell'avviare un daemon come root, chiamare chroot() per confinarlo in una sandbox, e poi abbassare i privilegi con posix_setuid() / posix_setgid() in modo che il processo nella jail non giri più come root.
chroot() vs open_basedir
Questi due meccanismi vengono spesso confusi. Risolvono un problema simile a livelli molto diversi:
chroot() | open_basedir | |
|---|---|---|
| Livello | Radice del processo a livello di sistema operativo | Controllo del percorso nel motore PHP |
| Dove si imposta | Nel codice a runtime | php.ini, .htaccess, pool FPM |
| Funziona con web SAPI | No (solo CLI/CGI) | Sì |
| Richiede privilegi di root | Sì | No |
| Robustezza | Jail imposta dal sistema operativo | Indicativa, può essere indebolita dai symlink |
Se devi solo mantenere una normale richiesta web all'interno di una directory, open_basedir è lo strumento pratico da usare. Usa chroot() quando gestisci un processo CLI e vuoi un vero confine a livello di sistema operativo.
Limitazioni e Avvertenze
- Non è un confine di sicurezza perfetto. Un processo che gira ancora come root all'interno di un chroot può spesso fuggire. Abbassa sempre i privilegi dopo aver applicato la jail.
- Dipendenze mancanti. La jail non ha
/lib,/etc,/usra meno che non li inserisca tu. Le funzioni che dipendono da file di sistema (lookup DNS, dati di localizzazione, fusi orari, librerie dinamiche) potrebbero fallire all'interno della jail. - Irreversibile per il processo. Non esiste un
unchroot(); il cambiamento dura per tutta la vita del processo. - Dipende dall'ambiente. Poiché la disponibilità dipende dal SAPI e dal sistema operativo, proteggi le chiamate e verifica il valore restituito invece di dare per scontato il successo.
Conclusione
chroot() confina un processo PHP in un singolo albero di directory cambiando la sua radice in quella directory e reimpostando la directory di lavoro su /. È un potente strumento di isolamento a livello di sistema operativo per i daemon CLI privilegiati, ma richiede i permessi di root, è limitato a CLI/CGI e non è disponibile su Windows. Per le restrizioni per singola richiesta in un normale stack web, usa invece open_basedir e tratta chroot() come uno strato di una strategia di difesa in profondità. Per saperne di più sul lavoro con i percorsi e il filesystem, consulta chdir(), getcwd() e il capitolo sul filesystem PHP.
Diagramma
Ecco come chroot() modifica ciò che un processo può raggiungere:
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 /];Nota: il confine è imposto dal sistema operativo, quindi si applica a ogni operazione sui file effettuata dal processo, non solo alle chiamate di funzione PHP.