Какова проблема безопасности с моим кодом?

Несколько лет назад я отправил ответ на вопрос о способе, в PHP, чтобы позволить пользователю передать в URI относительный путь к файлу для загрузки, предотвращая обход каталога.

У меня появилось несколько комментариев, в которых говорится, что код небезопасен, и несколько downvotes (последнее из сегодняшних событий). Здесь код:

$path = $_GET['path'];
if (strpos($path, '../') !== false ||
    strpos($path, "..\\") !== false ||
    strpos($path, '/..') !== false ||
    strpos($path, '\..') !== false)
{
    // Strange things happening.
}
else
{
    // The request is probably safe.
    if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . $path))
    {
        // Send the file.
    }
    else
    {
        // Handle the case where the file doesn't exist.
    }
}

Я снова и снова просматривал код, тестировал его и до сих пор не понимаю, что представляет собой проблема безопасности.

Единственный намек, который я получил в комментариях, заключается в том, что ../ можно заменить на %2e%2e%2f. Это не проблема, так как PHP автоматически преобразует ее в ../.

В чем проблема с этим фрагментом кода? Каким может быть значение ввода, которое могло бы позволить обход каталога или что-то сломать?

Ответ 1

Я только что запустил ваш код через Burp intruder и в этом случае не может найти его.

Вероятно, он был отклонен из-за эксплойтов против других/старых стеков технологий, которые использовали подобный подход, занесенный в черный список некоторых комбинаций символов.

Как вы уже упоминали, текущая версия PHP автоматически расшифровывает входные данные, но есть недостатки, когда такие методы, как двойное кодирование URL (точка = %252e), 16-битное кодирование Unicode (точка = %u002e), перекрытие Кодировка Юникода UTF-8 (точка = %c0%2e) или вставка нулевого байта (%00) может обмануть фильтр и разрешить код на стороне сервера интерпретировать путь как незашифрованную версию, как только ей были даны большие пальцы вверх фильтр.

Вот почему он установил звонки колоколов. Несмотря на то, что ваш подход работает здесь, как правило, это может быть не так. Технология всегда меняется, и всегда лучше ошибаться на стороне осторожности и использовать методы, которые невосприимчивы к интерпретации набора символов, где это возможно, например, используя белые списки известных хороших символов, которые, вероятно, будут всегда хороши или использовать функцию файловой системы (realpath упоминалось в связанном ответе), чтобы убедиться, что фактический путь - тот, который вы ожидаете.

Ответ 2

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

.htaccess
some-secret-file-with-a-password-in-it.php

Другими словами, все в каталоге или подкаталоге будет доступно, включая .htaccess файлы и исходный код. Если что-либо в этом каталоге или его подкаталогах не должно быть загружаемым, то это отверстие безопасности.

Ответ 3

Я не могу думать о любом случае, когда это может потерпеть неудачу.

Тем не менее, я не знаю, как PHPs file_exists реализуется внутри, и имеет ли он некоторые в настоящее время неизвестные причуды. Точно так же, как PHP имел проблемы с нулевым байтом с некоторыми функциями файловой системы до PHP 5.3.4.

Итак, чтобы играть в это безопасно, Id скорее хотел бы проверить уже разрешенный путь, а не слепо доверять PHP и, возможно, более важно - мое предположение, четыре упомянутые последовательности являются единственными, которые могут привести к тому, что путь выше назначенный базовый каталог. Вот почему я предпочел бы ircmaxells solution для .

Ответ 4

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

if(preg_match('/^[A-Za-z0-9\-\_]*$/', $path) ) {
    // Yay
} else {
    // No
}

Или, альтернативно:

switch($path) {
    case 'page1':
    case 'page2':
        // ...
        break;
    default:
        $path = 'page1';
        break;
}

include $path;