flawopen.com/Включение файлов/PHP include()
Безопасен ли PHP include() с пользовательским вводом?
Нет. include() разбирает выбранный файл как PHP, но последствия зависят от доступного файла и его содержимого. Ошибка может раскрыть исходный текст через wrapper, подключить неожиданный локальный файл или выполнить контролируемый атакующим PHP-код только если он может поместить содержимое с PHP-тегами в подключаемый файл. Подключение удалённого URL также зависит от конфигурации PHP. Сопоставляйте выбранный пользователем ключ с фиксированным списком; не собирайте путь include из данных запроса.
<?php // User-controlled path: unintended local file include $_GET['page'] . '.php'; // Example selector: ../../private/sample // Impact depends on the resolved file and // whether it contains PHP code to interpret. // Remote URL inclusion requires the relevant // URL wrapper and allow_url_include setting. // Wrappers can change which resource is read; // php://filter is a read filter, not a path // containment check.
<?php // Allowlist: input selects a KEY, // never a path or wrapper. $pages = [ 'home' => __DIR__ . '/pages/home.php', 'about' => __DIR__ . '/pages/about.php', 'contact' => __DIR__ . '/pages/contact.php', ]; $key = $_GET['page'] ?? 'home'; if (!is_string($key) || !array_key_exists($key, $pages)) { http_response_code(404); exit; } include $pages[$key];
Почему локальное включение не означает автоматическое выполнение кода
Локальное включение само по себе не означает выполнение кода. PHP разбирает подключаемый файл: инструкции в корректных PHP-тегах выполняются в контексте вызывающего файла, остальные байты могут быть выведены. Для выполнения нужен доступный файл с контролируемым PHP-кодом или другое эксплуатируемое поведение. Уязвимость загрузки или записи журнала может стать частью цепочки, если помещает контролируемый PHP-текст в файл, который позднее подключается.
php://filter применяет фильтр при открытии потока. В уязвимом сценарии чтения или включения подходящий фильтр может раскрыть исходный текст без выполнения; доступ зависит от wrapper, разрешений и конфигурации.
Почему очистка пути не решает проблему
Удаление ../, добавление фиксированного расширения или префикса каталога не доказывает, что итоговый ресурс разрешён. Кодировки, абсолютные пути, правила платформы и wrappers меняют интерпретацию. Уберите контроль пользователя над именем ресурса: разрешайте только фиксированные ключи приложения.
Если путь необходимо разрешить, канонизируйте его через realpath() и проверьте границу каталога, как описано в руководстве Path Traversal. Ограничьте результат разрешёнными файлами и учитывайте symlink и гонки между проверкой и использованием. Фиксированный список целей безопаснее.
Частые вопросы
Эта настройка отключает URL-wrappers для include/require. В PHP она выключена по умолчанию и объявлена устаревшей с версии 7.4. Это снижает риск удалённого включения, но не предотвращает локальный traversal и небезопасные локальные include. Оставьте её выключенной и исключите контроль пользователя над целью.
PHP 5.3.4 исправил завершение пути нулевым байтом. Не полагайтесь на исчезновение этого старого приёма: недоверенные цели include всё ещё опасны.
require и варианты _once также подключают и разбирают цель; их обработка ошибок и повторного включения отличается, но недоверенную цель они не обезвреживают. readfile() и file_get_contents() возвращают данные, не разбирая их как PHP; недоверенный путь может вызвать traversal или раскрытие данных, но не выполнение через include.