У меня есть базовый путь/безотносительно/foo/
и
$_GET['path']
должен относиться к нему.
Как мне это сделать (чтение каталога), не разрешая обход каталога?
например.
/\.\.|\.\./
Не будет правильно фильтроваться.
У меня есть базовый путь/безотносительно/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. Только после этого я начинаю проверять атаки обхода, сопоставляя их с базовым путем, который я предопределил.
Вы можете попробовать использовать regex для удаления всех.. /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
(Все это предполагает, что вы говорите о пользователях)
положить нулевой index.htm для блока -Index
фильтр sQS при запуске
// Path Traversal Attack
if( strpos($_SERVER["QUERY_STRING"], "../") ){
exit("P.T.A. B-(");
}
Рассматривая создание новых файлов или папок, я подумал, что могу использовать двухэтапный подход:
Сначала проверьте попытки обхода, используя пользовательскую реализацию функции, подобной realpath()
, которая, однако, работает для произвольных путей, а не только для существующих файлов. Там хорошая отправная точка здесь. Расширьте его с помощью urldecode()
и всего, что, по вашему мнению, стоит проверить.
Теперь, используя этот грубый метод, вы можете отфильтровать некоторые попытки обхода, но возможно, что вы пропустили какую-то хакерскую комбинацию специальных символов, символических ссылок, экранирующих последовательностей и т.д. Но поскольку вы точно знаете, что целевой файл не существует (проверьте с помощью file_exists
) никто не может ничего перезаписать. В худшем случае кто-то может получить ваш код, создавая файл или папку где-нибудь, что в большинстве случаев может быть приемлемым риском, если ваш код не позволяет им сразу записывать в этот файл/папку.
Наконец, теперь путь указывает на существующее местоположение, поэтому теперь вы можете выполнить правильную проверку, используя методы, предложенные выше, используя realpath()
. Если в этот момент выясняется, что произошел обход, вы все еще в большей или меньшей степени в безопасности, при условии, что вы предотвращаете любые попытки записи в целевой путь. Также прямо сейчас вы можете удалить целевой файл /dir и сказать, что это была попытка обхода.
Я не говорю, что его нельзя взломать, так как, в конце концов, он может разрешить внесение незаконных изменений в ФС, но все же лучше, чем просто делать пользовательские проверки, которые не могут использовать realpath()
, и окно для злоупотреблений оставить открытым создание временного и пустого файла или папки где-то ниже, чем предоставление им возможности сделать его постоянным и даже записать в него, как это будет происходить только с пользовательской проверкой, которая может пропустить некоторые крайние случаи.
Также поправьте меня, если я ошибаюсь, пожалуйста!