Регулярное выражение, чтобы проверить, идет ли путь только вниз


Я хочу проверить, идет ли путь, указанный пользователем, следующим образом:

my/down/path

В противоположность:

this/path/../../go/up

По соображениям безопасности.

Я уже сделал это:

return (bool)preg_match('#^([a-z0-9_-])+(\/[a-z0-9_-])*$#i', $fieldValue);

Но пользователю должно быть разрешено использовать '.' на своем пути (например: my/./path, что не полезно, но он может), и я не знаю, как это рассматривать.

Затем я ищу безопасное регулярное выражение, чтобы проверить это.

Спасибо

Редактировать: После просмотра ответов, да, это было бы хорошо если тест проверяет, является ли реальный путь (удаление '.' и '..') путем вниз.

Author: T.Rob, 2011-09-25

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 работает только в том случае, если имя пути уже существует.

 2
Author: NikiC, 2011-09-25 12:49:47

Вы можете просто проверить, начинается ли реальный путь указанного пользователем пути с разрешенного пути:

function isBelowAllowedPath($allowedPath, $pathToCheck)
{
    return strpos(
        realpath($allowedPath . DIRECTORY_SEPARATOR . $pathToCheck), 
        realpath($allowedPath)
    ) === 0;
}

Демонстрация на кодовом контроллере

Обратите внимание, что это также вернет false для каталогов, которые не существуют ниже $allowedPath.

 4
Author: Gordon, 2011-09-25 13:07:18
$folders = $explode('/', $path);

if (in_array('..', $folders)) {
    print('Error: path contains ..');
}
 0
Author: PeeHaa, 2011-09-25 12:29:04

Если вы хотите только запретить пользователю подниматься по иерархии путей, вы можете явно выполнить поиск '..':

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

 0
Author: Alexander Olsson, 2011-09-25 12:40:40

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

Например,

  • Вас интересует переход по жестким или мягким ссылкам ниже текущего рабочего каталога?
  • Поддерживает ли система, в которой вы находитесь (или в которой потенциально можете быть развернуты), юникод?
  • Как многие вещи оценивают рассматриваемую строку до или после того, как ваш PHP-код ее увидит? Веб-сервер? Раковина? Что-то еще?

Предположим, я отправлю вашему сценарию строку типа ./..%2f../? Важно ли для вашего приложения, чтобы эта строка подняла меня на два уровня? Или что сценарии, представленные в других ответах, не поймают это, потому что это не соответствует ..?

Как насчет ./\.\./? Если путь анализируется путем разделения на \ и /, сценарий в принятый ответ не поймает его, потому что каждая часть будет выглядеть как ., которая является просто текущим каталогом. Но типичная оболочка UNIX обрабатывает \ как escape-символ, поэтому передача его ./\.\./ эквивалентна ./../, и поэтому злоумышленник может использовать тот факт, что сценарий сочетает в себе тесты для путей в стиле UNIX и Windows.

Если под "безопасностью" вы действительно подразумеваете, что хотите обеспечить защиту от случайных ошибок и опечаток, то других ответов, вероятно, достаточно. Если вы программирование для враждебной среды и хотите предотвратить нарушения от преднамеренных атак, тогда они едва заметны, и вам было бы хорошо посоветовать прочитать о безопасном программировании в OWASP. Я бы начал с их статей о прохождении пути, а затем прочитал о других атаках, которые они описывают, а также о том, как их избежать и, что более важно, как их проверить.

 0
Author: T.Rob, 2011-09-25 15:45:15