assert在旧版php中可被当作eval使用,因接收字符串参数时会执行代码,造成任意命令执行风险;php 7.2+已废弃该行为,8.0+彻底移除,防御需升级版本、禁用动态assert或重定义函数。

assert 本身不是 eval 的替代品,但在某些旧版 PHP(如 5.x 和早期 7.x)中,assert() 接收字符串参数时会将其当作代码执行,这确实曾被用作绕过简单 WAF 或规避 eval 检查的手段。Python 中的 assert 完全不支持这种行为——它只做断言判断,不会执行任意代码。所以问题核心其实是:如何防止这类“伪 eval”式危险调用在其他语言或混用场景中被滥用。
PHP 中 assert 当 eval 用的风险本质
在受影响的 PHP 版本里,写法如 assert("system('ls')") 等价于执行 shell 命令。这不是 assert 的设计本意,而是历史遗留的解析逻辑漏洞。它的危害和 eval 高度相似:输入可控 → 字符串被解释执行 → 任意命令执行。
- 攻击者常利用它绕过只检测
eval、exec等关键词的静态扫描规则 - 只要传入的是字符串,且 PHP 版本未禁用该特性,就可能触发代码执行
- 与 eval 一样,它默认拥有当前上下文的所有权限(文件读写、网络、系统调用)
关键防御动作:关掉危险模式
PHP 7.2+ 已废弃字符串形式的 assert,并在 8.0+ 彻底移除。但若仍在维护旧系统,必须主动加固:
- 升级 PHP 版本:优先迁移到 8.0 或更高版本,assert 只接受布尔表达式
-
禁用动态 assert:在 php.ini 中设置
assert.active = On(保持启用),但加上zend.assertions = -1(完全关闭运行时 assert 解析) -
运行时重定义 assert:在入口文件开头加
function assert($e) { return (bool)$e; },覆盖原函数,使其失去执行能力
统一原则:不信任任何“看似无害”的动态执行点
eval、assert(旧版)、create_function、preg_replace 的 /e 修饰符、甚至某些模板引擎的表达式求值,都属于同一类风险接口。防御不能只盯名字,而要看“是否把用户输入当代码执行”:
- 对所有接收字符串并执行的函数做白名单审查,确认其输入来源是否绝对可信
- 用静态分析工具(如 PHPStan、psalm)扫描项目中所有 assert() 调用,检查参数是否为字面量或拼接字符串
- Web 应用中,凡涉及用户输入参与表达式构造的地方,一律改用安全解析器:比如数学表达式用
expr-parser,结构化数据用json_decode()或ast.literal_eval(Python)
本质上,防不住“用 assert 当 eval”,是因为没管住“让不可信输入驱动代码执行”这个根问题。堵住一个入口,攻击者会找下一个。真正有效的做法是:默认拒绝动态执行,只在极少数明确场景下,用受控、窄域、可审计的方式放开。











