eval() 本质不安全,白名单过滤无效;应禁用eval,改用json_decode、filter_var或symfony/expression-language等安全替代方案,必要时用AST解析严格校验表达式结构。
eval() 本身无法安全,白名单过滤不是补救措施而是掩耳盗铃
PHP 的
执行任意字符串为 PHP 代码,只要输入可控,就等于把服务器控制权交给调用方。所谓“严格白名单过滤”,在
场景下本质是幻觉——正则或字符串检查拦不住绕过(比如用
拼出
,再拼接函数名),更挡不住动态变量、花括号语法、
表达式注入等真实攻击路径。
常见错误现象:
– 写了个“只允许数字和加减乘除”的正则,结果用户传入
成功执行命令
– 用
检查函数名白名单,但没禁用
或
间接调用
– 过滤掉
却忘了
、
、
、
真正安全的替代方案:不用
—— 改用
解析结构化数据,或用
+ 类型校验处理表达式输入
如果必须动态计算数学表达式,用现成库如
,它不走
,而是解析 AST 执行
所有“过滤后 eval”逻辑,都应视为高危代码,上线前必须进安全审计清单
真要硬上 eval(),必须剥离全部 PHP 上下文
如果你继承了遗留系统、无法替换又必须保留 eval 行为(极少数场景,如内部 DSL 调试工具),唯一能压低风险的做法,是让
只能接触一个完全隔离、无副作用的变量空间,且禁止任何语言结构扩展。
使用场景:
– 仅限本地开发环境下的配置模板渲染(非 Web 请求入口)
– 输入来源绝对可信(如硬编码字符串、CI 环境变量,且不含用户输入)
立即学习
“
PHP免费学习笔记(深入)
”;
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
永远不要传入
、
、
等超全局变量到
作用域
显式传入空作用域:
比
少一层失控风险(至少强制 return)
用
配合
限制,但这只是辅助,不能替代逻辑层防护
PHP 8.1+ 可配合
或自定义 opcache hook 记录
调用栈,用于事后追溯
白名单过滤 eval 输入?别信正则,信 AST 解析
想靠
拦住危险表达式?它连
都拦不住(分号后语句仍会执行)。真正能约束表达式的,只有解析其语法结构。
参数差异:
– 正则匹配:表面字符合规,实际可嵌套任意 PHP 结构
– AST 解析(如
):能确认是否仅为
等安全节点,拒绝
、
、
中含动态内容
用
解析输入,遍历
下的
节点
只允许
、
、
、
(且值不含
、
、
)
禁止任何
、
、
—— 它们可能引入变量引用或副作用
性能影响明显:一次 AST 解析比正则慢 10–50 倍,别在高频请求路径用
替代 eval 的三个务实选择(按优先级排序)
别纠结“怎么安全地 eval”,直接换掉。大多数声称需要
的业务,其实只需要其中一种能力:计算、映射、或模板填充。
纯数学表达式 → 用
:
这种写法它原生支持,底层不碰
字段映射/条件判断 → 提前定义规则数组:
,运行时查表,不解释字符串
模板类需求 → 用
或
,它们编译为 PHP 文件但绝不
用户模板,沙箱机制更可控
最容易被忽略的一点:很多团队把“用户可配公式”功能塞进
,其实数据库里存的是字段名和操作符,运行时拼成数组结构再交给 expression-language 执行——这一步抽象,就是安全边界的起点。
eval()eval()chr(97).chr(112).chr(112)app${...}"${@system('id')}"in_array()call_user_funcarray_mapsystemexecshell_exec`ls`proc_openeval()json_decode()filter_var()symfony/expression-languageeval()eval()$GLOBALS$_SERVER$_POSTeval()eval('return ' . $expr . ';');eval($expr);ini_set('disable_functions', 'exec,system,passthru,shell_exec,proc_open,popen')open_basedirpcoveval()preg_match('/^[0-9+\-*/().\s]+$/', $expr)1+2;phpinfo()nikic/php-parserExpr_BinaryOp_PlusExpr_FuncCallExpr_VariableScalar_StringPhpParser\ParserFactory::create(ParserFactory::PREFER_PHP7)Stmt_ExpressionExprExpr_BinaryOp_*Scalar_LNumberScalar_DNumberScalar_String${{;Expr_ArrayExpr_ClosureExpr_Ternaryeval()symfony/expression-language(price * quantity) - discounteval()['status' => ['map' => ['1' => 'active', '0' => 'inactive']]]league/platestwig/twigeval()eval()