PHP 8.3: Вопросы безопасности при разборе пользовательских INI-строк и файлов

Источник: «Security considerations when parsing user-provided INI strings and files»
PHP предоставляет функции parse_ini_string и parse_ini_file, которые повторно используют встроенный в PHP синтаксический анализатор, применяемый для собственных конфигурационных файлов PHP на основе INI.

Помимо разбора текста, парсер INI поддерживает наследование значений системного окружения и констант PHP, объявленных к моменту разбора текста. Начиная с версии PHP 8.3, он также поддерживает синтаксис запасных (fallback) значений для переменных окружения.

; Нормальные строковые литералы
my_config_name = normal

; Наследование переменной окружения SESSION_NAME или "", если она недоступна
my_config_name = ${SESSION_NAME}

; Наследование переменной окружения SESSION_NAME с запасным значением "MyDefaultValue"
my_config_name = ${SESSION_NAME:-MyDefaultValue}

; Интерполяция строк с переменными окружения
my_config_name = "${MAIL_FROM_USER}@${MAIL_FROM_DOMAIN}"

; Наследование PHP-константы PHP_VERSION
my_config_name = PHP_VERSION

Хотя эти усовершенствования полезны для конфигурирования PHP с помощью переменных окружения и использования доступных PHP-констант, использование встроенного PHP-парсера для анализа INI-значений, предоставленных пользователем, может стать уязвимостью безопасности, поскольку PHP можно обманом заставить раскрыть переменные окружения и PHP-константы, которые, скорее всего, содержат конфиденциальные данные, не подлежащие раскрытию.

Например, конфигурационный файл, предоставленный пользователем или удалённым сервером, которому нет полного доверия, может использовать это для того, чтобы обмануть сервер парсинга и раскрыть свои собственные переменные окружения и константы PHP:

; config.ini
plugin.name = "Free plugin ${DATABASE_NAME} / ${DATABASE_PASSWORD}"
plugin.description = DATABASE_PASSWORD
$conig = parse_ini_file('config.ini');
array(2) {
["plugin.name"]=> string(33) "Free plugin MyDbName / MyPa$$word"
["plugin.description"]=> string(10) "MyPa$$word"
}

Однако в PHP предусмотрены параметры конфигурации, позволяющие отключить принудительное использование типов и подстановку окружения/констант. Третий параметр функций parse_ini_file и parse_ini_string принимает битовую маску, и одним из принимаемых флагов является INI_SCANNER_RAW, который отключает парсинг PHP типов, переменных окружения и констант PHP:

; config.ini
plugin.name = "Free plugin ${DATABASE_NAME} / ${DATABASE_PASSWORD}"
plugin.description = DATABASE_PASSWORD
$conig = parse_ini_file('config.ini', scanner_mode: INI_SCANNER_RAW);
// или
$conig = parse_ini_file('config.ini', false, INI_SCANNER_RAW);
array(2) {
["plugin.name"]=> string(51) "Free plugin ${DATABASE_NAME} / ${DATABASE_PASSWORD}"
["plugin.description"]=> string(17) "DATABASE_PASSWORD"
}

Предосторожность здесь заключается в том, что PHP не использует по умолчанию флаг INI_SCANNER_RAW, а значит, все вызовы функций, не передающие явно флаг INI_SCANNER_RAW, будут уязвимы, если они анализируют INI-значения, предоставленные пользователем.

Дополнительные материалы

Предыдущая Статья

Планирование периодической очистки базы данных на Laravel

Следующая Статья

Повышение уровня TypeScript с помощью типов Record