最常见原因是配置未作用于真实运行的php实例:web(fpm/apache)与cli加载不同php.ini,fpm下www.conf中php_admin_value[disable_functions]优先级更高;必须通过phpinfo()或php -i确认路径,改后需systemctl restart php*-fpm,且配置项严禁空格、大小写错误或末尾逗号。

disable_functions 为什么写了却没生效
最常见原因是配置没落到真实运行的 PHP 实例上。Web 请求走的是 FPM 或 Apache 模块,CLI 走的是独立进程,两者加载的配置文件完全不同。phpinfo() 页面里 “Loaded Configuration File” 才是 Web 环境真正读的路径,不是 “Configuration File (php.ini) Path”。FPM 用户尤其要注意:www.conf 里的 php_admin_value[disable_functions] 会直接覆盖 php.ini,优先级更高。
验证方法必须分环境:Web 端看 phpinfo() 输出字段;CLI 端用 php -i | grep disable_functions;实测调用得用 php -r "var_dump(exec('id'));",不能只查 function_exists('exec') —— 后者返回 false 只说明函数不可见,不代表调用被拦截。
- 改完配置后必须
systemctl restart php*-fpm(或对应版本),reload不触发重读 - Docker 或 IDE 内置服务器(如
php -S)用的是完全独立的配置,需单独处理 - 宝塔等面板常把配置分散在多个路径,比如
/www/server/php/84/etc/php.ini和/www/server/php/84/etc/php-fpm.d/www.conf,漏改一个就失效
disable_functions 的写法错一个字符就全挂
这个配置项解析极脆弱,空格、大小写、逗号位置、换行都会导致整行失效。PHP 不做容错,而是静默丢弃非法部分——结果就是你写了十个函数,实际只禁了第一个,甚至一个都没禁。
disable_functions = exec,system,passthru,proc_open ✅disable_functions = exec, system, passthru ❌(逗号后空格让 system 变成带前导空格的字符串,匹配失败)disable_functions = EXEC,SHELL_EXEC ❌(函数名必须小写)disable_functions = exec,system, ❌(末尾逗号导致整行解析失败,值变为空)
- 函数名必须全小写:
exec、shell_exec、proc_open,大写或驼峰都不识别 - 逗号是唯一分隔符,前后绝对不能有空格
- 不能换行写,也不能加注释(
;后内容会被截断) - 推荐一次性写全:
exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec,putenv,ini_set,file_put_contents,copy,symlink,curl_exec
哪些函数真该禁,哪些禁了反而坏事
别照抄网上“全禁列表”。PHP 8.5.5 中很多函数已无实际危害(比如 create_function 在 7.2 就移除了),而有些函数禁了会让 Laravel、Composer、WordPress 直接崩溃。
真正高危且必须禁的三类函数:
— 进程执行:exec、system、shell_exec、passthru、proc_open、popen、pcntl_exec
— 动态代码:eval(语言结构,禁不了)、assert(PHP 8.3+ 仍支持字符串参数执行)
— 文件/网络操作:file_put_contents、copy、symlink、curl_exec(配合 SSRF 可读敏感文件)
-
unserialize不用禁——它本身不危险,危险的是反序列化链,禁了反而让业务逻辑崩掉 -
curl_init、scandir、getrusage这类 I/O 或调试函数,保留更稳妥,禁了会导致监控组件报错 -
dl()在 PHP 8.0+ 已移除,但若还在用 7.x,务必加入禁用列表 -
mail()看业务需求,禁了可能影响注册邮件发送,但若配合putenv可绕过 open_basedir,建议评估后决定
禁了函数还被绕过?这不是配置问题,是防线错了
disable_functions 只拦内置函数,对 eval、assert(字符串参数)、反序列化、扩展函数(如 imap_open 触发的 popen)、COM 对象(Windows)、LD_PRELOAD(Linux)完全无效。攻击者根本不用你禁掉的那几个“经典函数”。
真实绕过手段:
— proc_open 是首选:China Chopper、AntSword 默认先试它,返回资源句柄,日志不显眼
— putenv + mail() 或 error_log() 组合,在旧版本中可触发命令执行(CVE-2018-19537 类漏洞)
— Windows 下用 COM('WScript.shell'),Linux 下用 LD_PRELOAD 劫持动态链接库
— assert($_POST['x']) 在 PHP 8.3+ 仍能执行任意代码
- 单纯靠
disable_functions防 RCE 是纸糊的——它不防反序列化、不防 SSRF、不防第三方库漏洞 - 真正关键的是运行权限隔离:PHP 进程用低权限系统用户跑,禁止写 web 目录、禁止读
/etc/passwd等敏感路径 - 必须配套关闭
allow_url_fopen、allow_url_include,并设置open_basedir
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











