Регулярное выражение, чтобы проверить, идет ли путь только вниз
Я хочу проверить, идет ли путь, указанный пользователем, следующим образом:
my/down/path
В противоположность:
this/path/../../go/up
По соображениям безопасности.
Я уже сделал это:
return (bool)preg_match('#^([a-z0-9_-])+(\/[a-z0-9_-])*$#i', $fieldValue);
Но пользователю должно быть разрешено использовать '.'
на своем пути (например: my/./path
, что не полезно, но он может), и я не знаю, как это рассматривать.
Затем я ищу безопасное регулярное выражение, чтобы проверить это.
Спасибо
Редактировать: После просмотра ответов, да, это было бы хорошо если тест проверяет, является ли реальный путь (удаление '.'
и '..'
) путем вниз.
5 answers
Вы, вероятно, не хотите проверять, что путь не содержит ..
, но вместо этого хотите проверить, что, если он оценивается как целое, он не увеличивается. Например, ./path/..
все еще находится в .
, даже если он содержит ..
.
Вы можете найти реализацию проверки глубины пути в Twig:
$parts = preg_split('#[\\\\/]+#', $name);
$level = 0;
foreach ($parts as $part) {
if ('..' === $part) {
--$level;
} elseif ('.' !== $part) {
++$level;
}
if ($level < 0) {
return false;
}
}
return true;
Twig не использует realpath
для проверки, потому что realpath
имеет проблемы с путями в архивах Phar. Кроме того, realpath
работает только в том случае, если имя пути уже существует.
Вы можете просто проверить, начинается ли реальный путь указанного пользователем пути с разрешенного пути:
function isBelowAllowedPath($allowedPath, $pathToCheck)
{
return strpos(
realpath($allowedPath . DIRECTORY_SEPARATOR . $pathToCheck),
realpath($allowedPath)
) === 0;
}
Демонстрация на кодовом контроллере
Обратите внимание, что это также вернет false
для каталогов, которые не существуют ниже $allowedPath
.
$folders = $explode('/', $path);
if (in_array('..', $folders)) {
print('Error: path contains ..');
}
Если вы хотите только запретить пользователю подниматься по иерархии путей, вы можете явно выполнить поиск '..':
if (1 === preg_match('/\.\./', $path)) {
/* path contains .. */
}
, что также быстрее, чем взрыв и in_array.
Сравнительный анализ:
<?php
$attempts = 100000;
$path = 'my/path/with/../invalid';
$t = microtime(true);
for ($i = 0; $i < $attempts; ++$i) {
$folders = explode('/', $path);
if (in_array('..', $folders)) {
/* .. in path */ ;
}
}
$end = microtime(true);
printf("in_array: %f\n", $end - $t);
$t = microtime(true);
for ($i = 0; $i < $attempts; ++$i) {
if (1 === preg_match('/\.\./', $path)) {
/* .. in path */ ;
}
}
$end = microtime(true);
printf("preg_match: %f\n ", $end - $t);
In_array: 0,088750
Предварительное совпадение: 0.071547
Предыдущие ответы (включая принятый) касаются глубины пути, но не прохождения пути. Поскольку в вопросе конкретно упоминается, что это для безопасности, то случайной проверки, подобной описанной до сих пор, может быть недостаточно.
Например,
- Вас интересует переход по жестким или мягким ссылкам ниже текущего рабочего каталога?
- Поддерживает ли система, в которой вы находитесь (или в которой потенциально можете быть развернуты), юникод?
- Как многие вещи оценивают рассматриваемую строку до или после того, как ваш PHP-код ее увидит? Веб-сервер? Раковина? Что-то еще?
Предположим, я отправлю вашему сценарию строку типа ./..%2f../
? Важно ли для вашего приложения, чтобы эта строка подняла меня на два уровня? Или что сценарии, представленные в других ответах, не поймают это, потому что это не соответствует ..
?
Как насчет ./\.\./
? Если путь анализируется путем разделения на \
и /
, сценарий в принятый ответ не поймает его, потому что каждая часть будет выглядеть как .
, которая является просто текущим каталогом. Но типичная оболочка UNIX обрабатывает \
как escape-символ, поэтому передача его ./\.\./
эквивалентна ./../
, и поэтому злоумышленник может использовать тот факт, что сценарий сочетает в себе тесты для путей в стиле UNIX и Windows.
Если под "безопасностью" вы действительно подразумеваете, что хотите обеспечить защиту от случайных ошибок и опечаток, то других ответов, вероятно, достаточно. Если вы программирование для враждебной среды и хотите предотвратить нарушения от преднамеренных атак, тогда они едва заметны, и вам было бы хорошо посоветовать прочитать о безопасном программировании в OWASP. Я бы начал с их статей о прохождении пути, а затем прочитал о других атаках, которые они описывают, а также о том, как их избежать и, что более важно, как их проверить.