У меня есть приложение PHP, которое использует параметр $_GET
для выбора файлов JS / CSS в файловой системе.
Если я отклоняю все запросы, в которых входная строка содержит ./
, \
или байт за пределами видимого 7-битного диапазона ASCII, это достаточно для предотвращения прохождения родительских каталогов при передаче пути к базовым (на основе C) файлам PHP ?
Я знаю об уязвимостях с нулевым байтом , но есть ли какие-либо другие альтернативные / искаженные символы кодирования, которые могут скрипеть этими проверками?
Вот основная идея (не производственный код):
$f = $_GET['f']; // eg "path/to/file.js" // goal: select only unhidden CSS/JS files within DOC_ROOT if (! preg_match('@^[\x20-\x7E]+$@', $f) // outside visible ASCII || false !== strpos($f, "./") // has ./ || false !== strpos($f, "\\") // has \ || 0 === strpos(basename($f), ".") // .isHiddenFile || ! preg_match('@\\.(css|js)$i@', $f) // not JS/CSS || ! is_file($_SERVER['DOCUMENT_ROOT'] . '/' . $f)) { die(); } $content = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/' . $f);
Обновление: мой вопрос действительно о том, как функции файловой системы C интерпретируют произвольные последовательности ASCII (например, если есть недокументированные escape-последовательности), но я понимаю, что это, вероятно, зависит от системы и, возможно, является неосуществимым на практике.
Для моей активной проверки дополнительно требуется, чтобы realpath($fullPath)
начинался с realpath($_SERVER['DOCUMENT_ROOT'])
, гарантируя, что файл находится в пределах DOC_ROOT, но целью этого сообщения было то, что он должен realpath()
(он оказался ненадежным в в разных средах), но при этом допускают необычные, но допустимые URI, такие как /~user/[my files]/file.plugin.js
.
Вы сами это упоминаете, но сравнение realpath
пути ввода с известным корнем – лучшее решение, о котором я могу думать. Realpath разрешит любые скрытые функции пути / файловой системы, включая символические ссылки.
При фильтрации входных данных для обеспечения безопасности всегда используйте «белые списки», а не переклички.
Вы должны отклонять все пути, которые не соответствуют /^([A-Za-z0-9_-]+\/?)*[A-Za-z0-9_-]+\.(js)|(css)?$/
.
Это позволит допускать нормальные сегментированные пути, где каждый сегмент имеет буквы, цифры или _-
.
Может потребоваться небольшая перестройка, но даже если вы ../../passwd
, basename()
будет изолировать ее. Затем вы можете поместить все файлы, которые хотите использовать в одной папке.
Учитывая ../../././././a/b/c/d.txt
, basename($f)
будет d.txt
; этот подход кажется мне более мудрым, вместо того, чтобы пытаться перехитрить пользователя и забыть о дыре.