When PHP starts up, it follows a specific order to load configuration files:
1. Compiled-in defaults
2. php.ini in PHP's installation directory
3. .user.ini files in the document root
4. Additional .ini files in scan_dir (if enabled)
The directive we're focusing on is scan_dir
, which allows PHP to load configuration files from additional directories, potentially overriding your main php.ini settings.
To completely disable the scanning of additional .ini files, you have several options:
Method 1: Empty the scan_dir directive
; In your main php.ini file
; For PHP 5.x and 7.x
scandir =
; For PHP 8.x
; This is now called php_scandir
php_scandir =
Method 2: Using PHP's -n flag
If you're running PHP via command line, you can use:
php -n your_script.php
The -n
flag tells PHP to ignore all configuration files (including additional .ini files).
For complete configuration lockdown, consider these approaches:
1. Set immutable flags for critical directives:
disable_functions = "exec,passthru,shell_exec,system"
disable_functions = ${PHP:disable_functions} ; Makes it immutable
allow_url_fopen = Off
allow_url_fopen = ${PHP:allow_url_fopen} ; Makes it immutable
2. Change php.ini permissions:
chmod 444 /usr/local/php/lib/php.ini
chown root:root /usr/local/php/lib/php.ini
Create a test script to verify no additional .ini files are loaded:
<?php
// List all loaded configuration files
$inifiles = php_ini_scanned_files();
if (empty($inifiles)) {
echo "No additional .ini files loaded - configuration locked down successfully!";
} else {
echo "Warning: Additional .ini files loaded: " . $inifiles;
}
?>
For ultimate control, you can compile PHP with hardcoded settings:
./configure \
--with-config-file-scan-dir= \
--disable-all \
--enable-cli \
--your-other-options
This completely removes the scan directory functionality at compile time.
Remember that:
- Disabling additional .ini files may break applications that rely on them
- Consider using php-fpm pools with different configurations instead
- Regularly audit your php.ini using tools like
php -i
orphpinfo()
PHP's configuration system is designed with flexibility in mind, allowing multiple .ini files to contribute to the final runtime configuration. The main php.ini file (typically at /usr/local/php/lib/php.ini
) is loaded first, followed by any additional .ini files found in the scan directory.
While this flexibility is useful in development environments, it can pose security risks in production when:
- Multiple sysadmins might accidentally override settings
- Malicious users could potentially add their own .ini files
- Configuration drift occurs between environments
To completely disable scanning for additional .ini files, add this to your main php.ini:
; Disable scanning for additional .ini files
scan_dir_for_ini = 0
Alternatively, you can set this at runtime in your Apache configuration or .htaccess:
<IfModule mod_php7.c>
php_admin_value scan_dir_for_ini 0
</IfModule>
For critical security settings that should never be overridden, PHP provides several approaches:
1. Using php_admin_value in Apache:
<VirtualHost *:80>
ServerName example.com
php_admin_value open_basedir "/var/www/example.com:/tmp"
php_admin_value disable_functions "exec,passthru,shell_exec,system"
</VirtualHost>
2. Compiling PHP with hardcoded defaults:
For maximum security, you can recompile PHP with certain settings hardcoded:
./configure --with-config-file-scan-dir=no \
--disable-all \
--enable-option-checking=fatal
After making changes, verify your configuration using:
<?php
// Check if additional ini scanning is disabled
if (ini_get('scan_dir_for_ini')) {
echo "Warning: Additional .ini scanning is still enabled!";
} else {
echo "Configuration locked successfully!";
}
// View the complete loaded configuration
phpinfo(INFO_CONFIGURATION);
?>