eval和assert禁不掉是因为它们是语言结构而非函数,disable_functions无法拦截;PHP 8.5下须组合内核级hook(zend_compile_string/zend_execute_ex)、禁用辅助函数及关闭allow_url_include等三重防线。

eval 和 assert 为什么禁不掉?
直接在 disable_functions 里写 eval,assert 是无效的——因为它们不是普通函数,而是语言结构(language construct),内核走的是 zend_compile_string 和 zend_execute_ex 路径,disable_functions 根本拦不住。你改完 php.ini、重启 PHP-FPM,用 phpinfo() 查还是能看到它们“可用”,这就是典型误判。
PHP 8.5 下真正有效的禁用方式
PHP 8.5 环境下,必须组合三类手段才可靠:
-
disable_functions仍要配,覆盖所有能辅助 RCE 的函数:比如exec,shell_exec,proc_open,file_put_contents,scandir,防止 Webshell 绕过 eval 执行后续操作 - 启用
suhosin扩展(仅兼容到 PHP 7.4)已不可行;PHP 8.5 必须用自研 C 扩展或官方推荐替代方案 - 核心防线是内核级拦截:hook
zend_compile_string拦所有字符串编译(含eval和assert的执行路径),同时 hookzend_execute_ex拦assert的运行时调用(因部分场景下assert可被zend.assertions=1触发)
绕过常见配置的真实案例
攻击者早就不靠 eval($_GET[x]) 这种裸写法了。以下几种方式在 PHP 8.5 下依然有效,且能绕过单纯 disable_functions:
- 用
${${phpinfo()}}或preg_replace('/.*/e', $_GET['c'], '')(虽/e修饰符已被移除,但某些旧扩展残留支持) - 通过
assert配合base64_decode+gzinflate混淆载荷,躲过 WAF 关键字匹配 - 利用
create_function(PHP 7.2+ 已废弃但未彻底删除)动态构造闭包,再 call_user_func 执行 - 若
allow_url_include=On,可通过include "data://text/plain,<?php phpinfo();?>"绕过所有函数禁用
生产环境必须检查的 3 个硬性配置点
光写代码没用,服务器层漏一点,前面全白干:
立即学习“PHP免费学习笔记(深入)”;
-
open_basedir必须设为最小权限路径(如/var/www/html:/tmp),且确保上传目录不在其中;否则即使禁了eval,Webshell 也能用file_get_contents读取/etc/passwd -
allow_url_fopen和allow_url_include必须均为Off,这是远程代码加载的总闸门 - Web 目录(如
/var/www/html)下所有子目录需取消执行权限:chmod -x upload/ cache/ logs/,只留www-data用户可写不可执行
最易被忽略的是:assert 在 zend.assertions=-1 时确实不执行,但很多运维误设成 1 或 0,而框架(如 ThinkPHP)可能在调试模式下主动开启它——这种隐式启用比明面调用更危险。



















