chroot()
Aprenda como a função chroot() do PHP altera o diretório raiz de um processo para criar um ambiente de sistema de arquivos isolado, com sintaxe e exemplos.
Função PHP chroot()
A função chroot() altera o diretório raiz do processo em execução atual para o diretório especificado e, em seguida, define o diretório de trabalho atual como /. Após a chamada, o processo não consegue mais ver ou acessar nenhum arquivo acima dessa nova raiz — ele fica "preso" dentro da árvore de diretórios. Essa técnica é comumente chamada de chroot jail.
Esta página explica o que chroot() faz, quando você usaria (e não usaria) essa função, sua sintaxe e as limitações que você precisa conhecer antes de depender dela.
Sintaxe
chroot(string $directory): bool| Parâmetro | Descrição |
|---|---|
$directory | O caminho para o diretório que se torna a nova raiz (/) do processo. |
Valor de retorno: true em caso de sucesso, false em caso de falha.
Requisitos
chroot() não está disponível em todos os ambientes. Antes de usá-la, tenha em mente estas restrições:
- Funciona apenas em SAPIs CLI e CGI — não está disponível na maioria dos SAPIs de módulo, como
mod_phpou PHP-FPM executando uma requisição web típica. - Não é implementada no Windows.
- O processo chamador precisa ter privilégios de root (superusuário). Um usuário normal não pode alterar o diretório raiz.
Por causa dessas limitações, chroot() é usada principalmente em daemons PHP CLI de longa duração e scripts de worker, não em código que lida com requisições HTTP comuns.
Exemplo Básico
Este script prende o processo dentro de /var/www/jail e, em seguida, lê um caminho relativo à nova raiz:
<?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";
}Após a chamada chroot(), o caminho /data/config.txt na verdade se refere a /var/www/jail/data/config.txt no sistema de arquivos real. O processo simplesmente não consegue expressar um caminho que escape da prisão.
Confirmando o Diretório de Trabalho
Como chroot() também move o diretório de trabalho para /, você pode confirmar a mudança com getcwd():
<?php
chroot('/var/www/jail');
echo getcwd(); // "/" (which is /var/www/jail on the real filesystem)Se você precisar de um diretório de trabalho diferente dentro da prisão, defina-o explicitamente com chdir() após a chamada chroot().
Por que Usar chroot()
O objetivo de chroot() é o isolamento. Uma vez que um processo está preso:
- Ele não pode abrir, ler ou gravar arquivos fora da nova raiz, mesmo com caminhos absolutos.
- Um bug ou exploit que tente atravessar diretórios (
../../etc/passwd) não tem para onde ir — não existe caminho acima de/. - Você pode fornecer uma árvore de diretórios mínima (apenas os arquivos de que o worker realmente precisa), reduzindo a superfície de ataque.
Um padrão comum é iniciar um daemon como root, chamar chroot() para confiná-lo em uma sandbox e, em seguida, abandonar os privilégios com posix_setuid() / posix_setgid() para que o processo preso não mais seja executado como root.
chroot() vs open_basedir
Esses dois recursos são frequentemente confundidos. Eles resolvem um problema semelhante em níveis muito diferentes:
chroot() | open_basedir | |
|---|---|---|
| Nível | Raiz do processo no sistema operacional | Verificação de caminho no mecanismo PHP |
| Onde definido | No código em tempo de execução | php.ini, .htaccess, pool FPM |
| Funciona em SAPIs web | Não (apenas CLI/CGI) | Sim |
| Precisa de privilégios de root | Sim | Não |
| Segurança | Prisão imposta pelo SO | Consultiva, pode ser enfraquecida por links simbólicos |
Se você apenas precisa manter uma requisição web normal dentro de um diretório, open_basedir é a ferramenta prática. Use chroot() quando você controla um processo CLI e deseja uma fronteira real no nível do SO.
Limitações e Armadilhas
- Não é uma fronteira de segurança perfeita. Um processo ainda em execução como root dentro de um chroot muitas vezes pode escapar. Sempre abandone os privilégios após a prisão.
- Dependências ausentes. A prisão não tem
/lib,/etc,/usra menos que você os coloque lá. Funções que dependem de arquivos do sistema (consultas DNS, dados de localização, fusos horários, bibliotecas dinâmicas) podem falhar dentro da prisão. - Irreversível para o processo. Não existe
unchroot(); a mudança dura por toda a vida do processo. - Dependente do ambiente. Como a disponibilidade depende do SAPI e do SO, proteja as chamadas e verifique o valor de retorno em vez de assumir o sucesso.
Conclusão
chroot() confina um processo PHP a uma única árvore de diretórios, alterando sua raiz para esse diretório e redefinindo o diretório de trabalho para /. É uma poderosa ferramenta de isolamento no nível do SO para daemons CLI privilegiados, mas requer root, está limitada a CLI/CGI e não está disponível no Windows. Para restrições por requisição em uma pilha web normal, use open_basedir e trate chroot() como uma camada de uma estratégia de defesa em profundidade. Para saber mais sobre como trabalhar com caminhos e o sistema de arquivos, veja chdir(), getcwd() e o capítulo PHP Filesystem.
Diagrama
Veja como chroot() remodela o que um processo pode acessar:
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: A fronteira é imposta pelo sistema operacional, por isso se aplica a todas as operações de arquivo que o processo realiza, não apenas às chamadas de funções PHP.