Security considerations when parsing user-provided INI strings and files

Published On31 Jul 2023

Security considerations when parsing user-provided INI strings and files PHP provides parse_ini_string and parse_ini_file functions that reuse PHP's built-in PHP parser it uses for PHP's own INI-based configuration files.

In addition to parsing the text, the INI parser supports inheriting system environment values and PHP constant declared by the time the text is parsed. Since PHP 8.3, it also supports a fallback value syntax for environment variables.


; Normal string literals
my_config_name = normal

; Inherit SESSION_NAME environment variable, or "" if unavailable
my_config_name = ${SESSION_NAME}

; Inherit SESSION_NAME environment variable with fallback value "MyDefaultValue"
my_config_name = ${SESSION_NAME:-MyDefaultValue}

; String interpolation with environment variables
my_config_name = "${MAIL_FROM_USER}@${MAIL_FROM_DOMAIN}"

; Inherit PHP_VERSION PHP constant
my_config_name = PHP_VERSION

While these enhancements are useful to configure PHP using environment variables, and to use available PHP constants using the PHP's built-in PHP parser on user-provided INI values can be a security vulnerability as PHP can be tricked to expose environment variables and PHP constants which are likely to contain sensitive data that should not be exposed.


For example, a configuration file that is provided by a user or a remote server that is not fully trusted can exploit this to trick the parsing server to expose its own environment variables and PHP constants:

; 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"
}

However, PHP provides configuration parameters to disable PHP's type coercion and environment/constant substitution. The third parameter of parse_ini_file and parse_ini_string functions accept a bitmask, and one of the flags accepted is INI_SCANNER_RAW, which disables PHP's parsing of types, environment variables, and PHP constants:

; 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);
// or
$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"
}

The security precaution here is that PHP does not default to the INI_SCANNER_RAW flag, which means all function calls that do not explicitly pass the INI_SCANNER_RAW flag will be vulnerable if they parse user-provided INI values.

Recent Articles on PHP.Watch

All ArticlesFeed 
PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian

PHP 8.4 Installation and Upgrade guide for Ubuntu and Debian

A guide for Debian and Ubuntu on how to install PHP 8.4 on a new server or how to upgrade an existing PHP setup to PHP 8.4.
How to fix `mysql_native_password` not loaded errors on MySQL 8.4

How to fix mysql_native_password not loaded errors on MySQL 8.4

How to fix the SQLSTATE[HY000] [1524] Plugin 'mysql_native_password' is not loaded errors caused in MySQL 8.4 no longer enabling the mysql_native_password plugin by default.
How to fix PHP Curl HTTPS Certificate Authority issues on Windows

How to fix PHP Curl HTTPS Certificate Authority issues on Windows

On Windows, HTTPS requests made with the Curl extension can fail because Curl has no root certificate list to validate the server certificates. This article discusses the secure and effective solutions, and highlights bad advice that can leave PHP applications insecure.
Subscribe to PHP.Watch newsletter for monthly updates

You will receive an email on last Wednesday of every month and on major PHP releases with new articles related to PHP, upcoming changes, new features and what's changing in the language. No marketing emails, no selling of your contacts, no click-tracking, and one-click instant unsubscribe from any email you receive.

Support PHP.Watch — If you find the articles, version information, Codex, and other PHP.Watch contributions useful, consider supporting through GitHub Sponsors. Your sponsorship helps dedicate more time to creating valuable content and improving the PHP community. Together, we can keep the momentum going — thank you for your support!

Thanks to the highest tier sponsor: @TomasVotruba for your generous support to keep PHP.Watch moving 💜