Войны между программистами php и perl кажется давно уже позади. Даже я стал программировать на php, напрочь забыв perl. Но это не значит, что меня всё устраивает в php. Например то, что скрипты на php могут не только легко содержать куски html-разметки, но и запускаться из любой папки на сервере, я до сих пор считаю серьёзной уязвимостью, накладывающей дополнительные обязательства на программиста, пишущего код. Наверняка, можно озадачится и серверными средствами ограничить запуск php-скриптов из всех директорий, кроме выбранной, (или, по крайней мере, прописать в правах доступа к папкам запрет на исполнение с наследованием этого свойства файлами), но я пока не встречал кода, который показал бы, что этим кто-то озадачивался. Как использовать эту особенность php, я для себя решил. А в этой статье рассмотрим, как подняться по всему дереву каталогов в корневую директорию с помощью php-скрипта, который, как увидим, может спокойно лазить по всему серверу и что-то там себе в делать тихим сапом (в зависимости от развитости и извращённости фантазии программиста, его написавшего).
Основные переменные и функции php для определения папки, в которой находится программа
Для начала рассмотрим две глобальные переменные php и одну функцию, которые позволяют быстро с помощью языка программирования определить место, где находится скрипт в дереве каталогов на сервере. Эксперимента ради, я загрузил скрипт глубоко в папки установленной Joomla, на которой работает этот сайт.
Итак, скрипт с названием delete.php (потому что после написания статьи будет удалён) лежит в папке \www\mb4.ru\templates\protostar\html\layouts\joomla\html\batch\, если рассматривать его путь от домашней директории пользователя.
В php версиях: PHP 4 >= 4.1.0, PHP 5, PHP 7 есть переменная $_SERVER, содержащая в себе массив с заголовками, путями и местоположениями скриптов. Записи в этом массиве создаются веб-сервером. Нет гарантии, что каждый веб-сервер предоставит любую информацию, но по умолчанию, рассматриваемые ниже два пути, хранящиеся в этом массиве, отдаются сервером «на ура».
$_SERVER['DOCUMENT_ROOT']- в этой переменной содержится директория корня документов, в которой выполняется текущий скрипт, в точности та, которая указана в конфигурационном файле сервера.
$_SERVER['SCRIPT_FILENAME']- в этой переменной содержится абсолютный путь к исполняемому скрипту.
Для проверки, как это работает, достаточно запустить php-скрипт, который выдаст на экран содержимое этих переменных:
<?php
echo "$_SERVER['DOCUMENT_ROOT'] . "<br>";
echo "$_SERVER['SCRIPT_FILENAME']";
?>
В результате получим нечто подобное:
/var/www/mb4/data/www/mb4.ru/var/www/mb4/data/www/mb4.ru/templates/protostar/html/layouts/joomla/html/batch/delete.php
То есть корневую папку пользователя, под которым запущен на выполнение скрипт и ВНИМАНИЕ (!) — полный путь к исполняемому скрипту от корня дерева каталогов сервера (!). Красота ;)
В php есть ещё одна полезная функция, которой мы воспользуемся ниже для подъёма к корню файловой системы сервера, которая получает имя текущего рабочего каталога. Это функция getcwd(), работающая во всех версиях php (PHP 4, PHP 5, PHP 7).
То есть, запустив код:
<?php
echo "getcwd()";
?>
Получим в качестве выполнения:
/var/www/mb4/data/www/mb4.ru/templates/protostar/html/layouts/joomla/html/batch
Всё тот же путь, по которому можно подняться до корня файловой системы.
Как получить список папок ветки дерева каталога, в котором лежит php-скрипт
Самый простой способ получить список папок ветки дерева каталога, в котором лежит php-скрипт, это воспользоваться стандартной функций php preg_split(), работающей во всех версиях этого языка (PHP 4, PHP 5, PHP 7). Эта функция разбивает строку по регулярному выражению и помещает результаты разбиения в массив. В нашем случае, в качестве разделителей папок однозначно используется /, так что и думать особо не нужно, используя для вывода функцию print_r() так, подробно описано в → этой статье:
<?php
echo '<pre>';
print_r( preg_split ( '/\//', getcwd() ) );
echo '</pre>';
?>
В итоге получим вывод этого массива, в котором будут содержаться все папки, встречающиеся по пути от файла до корня файловой системы:
Array
(
[0] =>
[1] => var
[2] => www
[3] => mb4
[4] => data
[5] => www
[6] => mb4.ru
[7] => templates
[8] => protostar
[9] => html
[10] => layouts
[11] => joomla
[12] => html
[13] => batch
)
Если в этот массив нужно включить и имя файла, то вместо getcwd() нужно использовать глобальную переменную $_SERVER['SCRIPT_FILENAME'].
Как подняться к корню каталога с помощью php, заходя в каждую папку
Вот тут мы и добрались до ответа на основой вопрос: «Как подняться к корню каталога с помощью php?» Для этого достаточно знать, что в операционных системах типа *nix ссылкой на предыдущую папку служит .., а в php есть функция, позволяющая менять каталог chdir(). То есть, для перехода на каталог выше, нужно указать скрипту: chdir ( '..' ). В итоге получим такой скрипт, который поднимается к корню каталога файловой системы, последовательно заходя во все встречные родительские папки:
<?php
while ( getcwd() != '/' ) {
echo getcwd() . "<br/>";
chdir ( '..' );
}
?>
Для того, чтобы программа не пыталась выйти за пределы операционной системы в открытый космос (всё равно не получится), в цикле while нужно указать ограничение, что дальше корня / даже не пытаться заходить: getcwd() != '/'. Иначе будет бесконечный цикл и программа будет тупо биться в конечную папку, пытаясь выйти за её пределы.
В результате наш скрипт бодро откроет все папки (и даже сможет по ним полазить, если нужно) и выдаст список того, в каких директориях он побывал:
/var/www/mb4/data/www/mb4.ru/templates/protostar/html/layouts/joomla/html/batch/var/www/mb4/data/www/mb4.ru/templates/protostar/html/layouts/joomla/html/var/www/mb4/data/www/mb4.ru/templates/protostar/html/layouts/joomla/var/www/mb4/data/www/mb4.ru/templates/protostar/html/layouts/var/www/mb4/data/www/mb4.ru/templates/protostar/html/var/www/mb4/data/www/mb4.ru/templates/protostar/var/www/mb4/data/www/mb4.ru/templates/var/www/mb4/data/www/mb4.ru/var/www/mb4/data/www/var/www/mb4/data/var/www/mb4/var/www/var
Резюме
Таким образом, разместив короткую программу на php в любое место на сервере, можно попасть в любую родительскую директорию. Что с этим делать и как можно использовать, как-нибудь в другой раз.
Напоследок полный работающий код для тестирования и дописывания этого php-скрипта. Скрипт безобидный: ничего не дописывает и не меняет на сервере, так что его можно использовать в качестве заготовки для написания более осмысленной программы, выполняющей определённые действия. ;)
<?php
echo "Текущая папка: <b>" . getcwd() . "</b><br/><br/>";
echo '<pre>';
print_r( preg_split ( '/\//', getcwd() ) );
echo '</pre>';
while ( getcwd() != '/' ) {
echo getcwd() . "<br/>";
chdir ( '..' );
}
echo "<br>\$_SERVER['DOCUMENT_ROOT'] = <b>" . $_SERVER['DOCUMENT_ROOT'] . "</b><br>";
echo "\$_SERVER['SCRIPT_FILENAME'] = <b>" . $_SERVER['SCRIPT_FILENAME'] . "</b>";
?>