必须禁用system、exec、shell_exec、passthru、proc_open、popen、pcntl_exec、assert等仍可落地执行的高危函数;file_put_contents、symlink、copy需按场景评估禁用;curl_init等I/O函数通常无需禁用。配置须全小写、无空格,修改FPM pool配置并重启php-fpm,且须CLI与Web双端验证生效。

必须禁用 exec、system、proc_open 等函数,否则 RCE 防御就是纸糊的——这不是加固建议,而是上线前的强制动作。
哪些函数真该禁,哪些其实不用动
别照抄网上“全禁列表”,PHP 8.3/8.4 中很多函数已无实际危害(比如 create_function 早在 7.2 就移除了)。重点盯住三类仍能落地执行的函数:
-
system、exec、shell_exec、passthru:直接调 shell,首当其冲 -
proc_open、popen、pcntl_exec:FPM 下常被绕过检测,必须禁 -
assert、eval:虽是语言结构,但assert在 PHP 8.3 仍支持字符串参数触发代码执行;eval本身禁不了,但禁assert能堵掉最常见入口 -
file_put_contents、symlink、copy:配合路径遍历或日志包含可写 WebShell,尤其当open_basedir没设时更危险
像 curl_init、getrusage、scandir 这类 I/O 或调试函数,禁了反而导致 Laravel Telescope、Composer 自检报错,保留更稳妥。
配置写法和生效验证最容易翻车的地方
写错一个空格、大小写或没重启服务,等于白配。常见失效原因:
立即学习“PHP免费学习笔记(深入)”;
-
disable_functions = exec, system❌ —— 英文逗号后有空格,PHP 会把system当成带前导空格的函数名,实际没禁掉 - 写成
EXEC或Eval❌ —— 函数名必须全小写,PHP 内置函数没有大写形式 - 只改
/www/server/php/84/etc/php.ini,却没改 FPM pool 配置 ❌ —— 在宝塔或标准 PHP-FPM 部署中,php_admin_value[disable_functions]在/etc/php/84/fpm/pool.d/www.conf里优先级更高,会覆盖 php.ini - 改完只 reload Nginx/Apache ❌ —— 必须
systemctl restart php84-fpm(或对应版本),CLI 和 Web 是两套进程,reload 不触发重读配置
验证是否真生效,别只跑 php -r "echo exec('id');":
- Web 端:访问含
phpinfo()的页面,搜索disable_functions字段,看值是否匹配 - CLI 端:
/www/server/php/84/bin/php -i | grep disable_functions,确认 CLI 实例也同步 - 实测调用:
/www/server/php/84/bin/php -r "var_dump(system('id'));",应输出NULL并报PHP Warning: system() has been disabled for security reasons
全局禁 vs 单站点放行,怎么选才不踩坑
业务真需要某个函数(比如某 CMS 插件依赖 proc_open),别直接删全局配置——风险扩散到所有站点:
- 全局配置只做最小集合:禁掉
exec、system、assert这些通用高危项 - 单站点放行:进宝塔该网站「设置」→「配置文件」→ 在 PHP 配置片段里加
php_admin_value[disable_functions] = exec,system,shell_exec(即显式列出要禁的,把proc_open剔除) - 绝对不要在站点配置里写
disable_functions =留空——这会清空全局禁用列表,等于全放开
注意:这种 per-site 放行只对 FPM 生效,Apache + mod_php 下不支持 php_admin_value,只能靠 .htaccess 或虚拟主机配置,但后者易被绕过,不推荐。
禁了函数之后,程序为啥不动了或静默失败
disable_functions 触发的是 Zend 引擎层拦截,不是用户态错误,所以:
- 警告无法用
@抑制,也无法被set_error_handler捕获 - 返回值恒为
NULL,不会抛Exception,后续代码照常执行 - 老程序常假设
exec()返回非空字符串,写成if (exec(...)) { ... }就会进错分支 - WordPress 主题编辑器、插件更新可能依赖
file_get_contents远程拉取信息,禁了allow_url_fopen才影响它,不是disable_functions的锅
上线前务必用真实业务流跑一遍:上传文件、保存设置、触发定时任务、查看日志写入——不能只测单个函数调用是否报错。
真正容易被忽略的点是:禁用函数只是第一层,open_basedir 没设或设得宽泛,攻击者仍能 file_get_contents('/etc/passwd');allow_url_include = On 留着,include('data://text/plain,...') 就能绕过所有函数禁用。这些配置必须同步关,且每项都得验证是否落到实际运行的 PHP 实例上。



















