У меня есть базовый путь / what / foo /
и $_GET['path']
должен относиться к нему.
Однако как это сделать (чтение каталога), не разрешая обход каталога?
например.
/\.\.|\.\./
Не будет правильно фильтроваться.
Ну, один из вариантов – сравнить реальные пути:
$basepath = '/foo/bar/baz/'; $realBase = realpath($basepath); $userpath = $basepath . $_GET['path']; $realUserPath = realpath($userpath); if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) { //Directory Traversal! } else { //Good path! }
В принципе, realpath()
будет разрешать предоставленный путь к реальному жесткому физическому пути (разрешая символические ссылки, ..
, /
, //
и т. Д.). Итак, если реальный путь пользователя не начинается с реального базового пути, он пытается сделать обход. Обратите внимание, что на выходе realpath
не будет никаких «виртуальных каталогов», таких как .
или …
Ответ ircmaxell был не совсем правильным. Я видел это решение в нескольких фрагментах, но у него есть ошибка, связанная с выходом realpath()
. Функция realpath()
удаляет разделитель трейлинг-каталога, поэтому представьте себе две непрерывные каталоги, такие как:
/foo/bar/baz/ /foo/bar/baz_baz/
Поскольку realpath()
удалит последний разделитель каталога, ваш метод вернет «хороший путь», если $_GET['path']
был равен «../baz_baz», поскольку это было бы что-то вроде
strpos("/foo/bar/baz_baz", "/foo/bar/baz")
Может быть:
$basepath = '/foo/bar/baz/'; $realBase = realpath($basepath); $userpath = $basepath . $_GET['path']; $realUserPath = realpath($userpath); if ($realUserPath === false || strcmp($realUserPath, $realBase) !== 0 || strpos($realUserPath, $realBase . DIRECTORY_SEPARATOR) !== 0) { //Directory Traversal! } else { //Good path! }
Недостаточно проверять шаблоны типа ../ или подобных. Возьмите «../», например, URI кодирует «% 2e% 2e% 2f». Если проверка вашего шаблона происходит до декодирования, вы пропустите эту попытку обхода. Есть и другие трюки, которые хакеры могут сделать, чтобы обойти проверку шаблонов, особенно при использовании закодированных строк.
Я добился наибольшего успеха, останавливая их, канонизируя любую строку пути до ее абсолютного пути, используя что-то вроде realpath (), как предполагает ircmaxwell. Только после этого я начинаю проверять атаки обхода, сопоставляя их с базовым путем, который я предопределил.
У вас может возникнуть соблазн попробовать и использовать регулярное выражение для удаления всех ../s, но есть некоторые хорошие функции, встроенные в PHP, которые будут выполнять гораздо лучшую работу:
$ page = basename (realpath ($ _ GET));
basename – удаляет всю информацию каталога из пути, например ../pages/about.php станет about.php
realpath – возвращает полный путь к файлу, например about.php станет /home/www/pages/about.php, но только в том случае, если файл существует.
В сочетании они возвращают только имя файла, но только если файл существует.
Я предполагаю, что вы имеете в виду, не позволяя пользователям перемещаться по каталогу да?
Если вы пытаетесь остановить свой собственный PHP от прохождения каталога, вы должны просто сделать работу php должным образом в первую очередь.
То, что вам нужно, чтобы остановить пользователей, – это модифицированный файл .htaccess …
Options -Indexes
(Все это предполагает, что вы говорите о пользователях)