必须禁用exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec,pcntl_fork,pcntl_waitpid,eval,assert,putenv,dl,ini_set,symlink,file_put_contents,copy等函数,且须小写、无空格、逗号紧连;同时检查FPM的www.conf覆盖、重启对应服务,并配合open_basedir、allow_url_include=Off等措施防绕过。

直接在 php.ini 里配 disable_functions 是最基础也最关键的一步,但光写几个函数名远远不够——配置错一个字符、没重启服务、或只改了 CLI 配置而漏掉 FPM,都会让防护形同虚设。
必须禁用的核心函数(小写+无空格+逗号紧连)
以下函数全部禁用,缺一不可,否则攻击者极易绕过:
- 命令执行类:exec,passthru,shell_exec,system,proc_open,popen
- 进程控制类:pcntl_exec,pcntl_fork,pcntl_waitpid
- 动态代码类:eval,assert,create_function(PHP 8.5+ 中 create_function 已废弃,但仍建议保留)
- 辅助绕过类:putenv,dl,ini_set,symlink,file_put_contents,copy(防止写 WebShell 或篡改环境)
配置生效的硬性前提
很多环境“写了却没禁”,问题不在函数名单,而在执行路径:
- FPM 环境下,
www.conf中的php_admin_value[disable_functions]会覆盖php.ini,必须检查并统一修改 - CLI 和 Web 请求加载的是两份独立配置,需分别验证:
php -i | grep "Loaded Configuration" - 改完必须重启对应服务:
systemctl restart php*-fpm(Apache 用户重启apache2) - Docker、宝塔、MAMP 等环境有专属配置路径,不能只盯默认
/etc/php/*/fpm/php.ini
禁用不是终点,配套措施才能防住绕过
攻击者一旦能上传文件(如 /tmp/shell.php),再用未被禁的 include 加载,disable_functions 就完全失效:
- 强制设置
open_basedir = /var/www/html:/tmp,禁止访问/etc、/usr等敏感路径 - 关闭远程包含:
allow_url_include = Off且allow_url_fopen = Off - Nginx + PHP-FPM 下确认
security.limit_extensions = .php,防.phtml、.php5绕过解析 - 业务层杜绝变量拼接 include:
include $_GET['page']必须改为白名单映射
怎么确认真禁掉了?别信“看起来没了”
仅查 function_exists('exec') 或看 phpinfo() 输出是无效的——函数存在 ≠ 可调用,函数不存在 ≠ 被拦截成功:
- Web 端实测:
- CLI 实测:
php -r "var_dump(@exec('id'));" - 组合穿透测试:
file_put_contents('/tmp/test.php', ''); include '/tmp/test.php';—— 若还能执行,说明整条防护链已断
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











