W3docs

chroot()

Узнайте, как функция chroot() в PHP изменяет корневой каталог процесса, создавая изолированную среду файловой системы. Синтаксис, примеры и ограничения.

Функция chroot() в PHP

Функция chroot() изменяет корневой каталог текущего выполняющегося процесса на указанный вами каталог, после чего устанавливает текущий рабочий каталог в /. После вызова процесс больше не может видеть или обращаться к файлам выше нового корня — он «заключён» внутри дерева каталогов. Эта техника обычно называется chroot-тюрьма.

На этой странице объясняется, что делает chroot(), когда следует (и не следует) её использовать, её синтаксис и ограничения, которые необходимо знать перед применением.

Синтаксис

chroot(string $directory): bool
ПараметрОписание
$directoryПуть к каталогу, который становится новым корнем (/) процесса.

Возвращаемое значение: true в случае успеха, false в случае ошибки.

Требования

chroot() доступна не везде. Перед использованием учитывайте следующие ограничения:

  • Работает только с CLI и CGI SAPI — недоступна для большинства модульных SAPI, таких как mod_php или PHP-FPM при обработке обычных веб-запросов.
  • Не реализована в Windows.
  • Вызывающий процесс должен иметь привилегии root (суперпользователя). Обычный пользователь не может изменить корневой каталог.

Из-за этих требований chroot() в основном используется в долгоживущих PHP CLI-демонах и рабочих скриптах, а не в коде, обрабатывающем обычные HTTP-запросы.

Базовый пример

Этот скрипт заключает процесс внутри /var/www/jail, а затем читает путь относительно нового корня:

<?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";
}

После вызова chroot() путь /data/config.txt фактически указывает на /var/www/jail/data/config.txt в реальной файловой системе. Процесс просто не может сформировать путь, выходящий за пределы тюрьмы.

Проверка рабочего каталога

Поскольку chroot() также перемещает рабочий каталог в /, вы можете подтвердить изменение с помощью getcwd():

<?php
chroot('/var/www/jail');

echo getcwd();   // "/"  (which is /var/www/jail on the real filesystem)

Если внутри тюрьмы нужен другой рабочий каталог, установите его явно с помощью chdir() после вызова chroot().

Зачем использовать chroot()

Цель chroot()изоляция. После того как процесс заключён в тюрьму:

  • Он не может открывать, читать или записывать файлы за пределами нового корня, даже по абсолютным путям.
  • Ошибка или эксплойт, пытающийся выполнить обход каталогов (../../etc/passwd), не найдёт куда идти — пути выше / не существует.
  • Можно использовать минимальное дерево каталогов (только файлы, необходимые рабочему процессу), сокращая поверхность атаки.

Распространённый паттерн — запустить демон от имени root, вызвать chroot() для помещения в песочницу, а затем снизить привилегии с помощью posix_setuid() / posix_setgid(), чтобы заключённый процесс больше не работал от имени root.

chroot() vs open_basedir

Эти функции часто путают. Они решают схожую задачу на принципиально разных уровнях:

chroot()open_basedir
УровеньКорень процесса на уровне операционной системыПроверка пути в PHP-движке
Где задаётсяВ коде во время выполненияphp.ini, .htaccess, пул FPM
Работает под веб-SAPIНет (только CLI/CGI)Да
Требует привилегий rootДаНет
НадёжностьТюрьма на уровне ОСРекомендательная, может быть обойдена через симлинки

Если вам нужно лишь ограничить обычный веб-запрос одним каталогом, open_basedir — практичный инструмент. Используйте chroot(), когда вы управляете CLI-процессом и хотите создать настоящую границу на уровне ОС.

Ограничения и подводные камни

  • Не является идеальной границей безопасности. Процесс, продолжающий работать от имени root внутри chroot, часто может выйти за её пределы. Всегда снижайте привилегии после заключения в тюрьму.
  • Отсутствующие зависимости. Тюрьма не имеет /lib, /etc, /usr, если вы их туда не поместите. Функции, зависящие от системных файлов (DNS-запросы, данные локали, часовые пояса, динамические библиотеки), могут не работать внутри тюрьмы.
  • Необратимо для процесса. Функции unchroot() не существует; изменение действует на протяжении всего времени жизни процесса.
  • Зависит от окружения. Поскольку доступность зависит от SAPI и ОС, защищайте вызовы и проверяйте возвращаемое значение, а не считайте выполнение успешным по умолчанию.

Заключение

chroot() ограничивает PHP-процесс единственным деревом каталогов, изменяя его корень на указанный каталог и сбрасывая рабочий каталог в /. Это мощный инструмент изоляции на уровне ОС для привилегированных CLI-демонов, однако он требует прав root, ограничен CLI/CGI и недоступен в Windows. Для ограничений на уровне запроса в обычном веб-стеке используйте open_basedir и рассматривайте chroot() как один из уровней стратегии глубокоэшелонированной защиты. Чтобы узнать больше о работе с путями и файловой системой, смотрите chdir(), getcwd() и главу PHP Filesystem.

Диаграмма

Вот как chroot() изменяет то, к чему процесс получает доступ:

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 /];

Примечание: граница обеспечивается операционной системой, поэтому она применяется ко всем файловым операциям процесса, а не только к вызовам PHP-функций.

Практика

Практика
Какова цель функции chroot в PHP?
Какова цель функции chroot в PHP?
Was this page helpful?