call_user_func()安全使用需白名单校验回调函数及参数:回调必须来自预定义数组且严格比较,禁止用户输入直接传入;类方法调用须通过映射表硬编码;所有参数按类型过滤校验,防止RCE。

call_user_func() 本身不是“坏函数”,但用错就容易变成远程代码执行(RCE)入口。关键不在禁用它,而在控制传入的回调参数是否可控、是否可信。
回调函数来源必须白名单化
最直接有效的办法:绝不让用户输入直接进第一个参数(callback)。
比如下面这种写法极危险:
call_user_func($_GET['func'], $_GET['arg']);
攻击者可传 ?func=system&arg=ls 直接执行命令。
✅ 正确做法是限定可调用的函数范围:
$allowed = ['json_encode', 'htmlspecialchars', 'intval', 'trim'];
if (in_array($_GET['func'], $allowed, true)) {
echo call_user_func($_GET['func'], $_GET['arg'] ?? '');
}⚠️ 注意:in_array 要加 true 严格比较,避免 '0' == 'system' 这类弱类型绕过。
禁止动态构造类方法调用
call_user_func() 支持数组形式回调,如 ['ClassName', 'method'] 或 [$obj, 'method']。
如果 $obj 或类名来自用户输入,可能触发未预期的 __invoke、__call 或反序列化链。
❌ 危险示例:
call_user_func([$_GET['class'], $_GET['method']]);
✅ 应该:
- 类名和方法名都走硬编码或配置项;
- 避免从任意请求参数拼装回调结构;
- 若必须支持多类方法,用映射表代替字符串拼接:
$map = [
'user' => ['UserHelper', 'formatName'],
'order' => ['OrderService', 'getSummary']
];
if (isset($map[$_GET['type']])) {
call_user_func($map[$_GET['type']], $_GET['data'] ?? []);
}参数传递要过滤/校验,尤其第二参数起
即使 callback 安全,后续参数若未经处理,也可能被用于注入(比如传给 file_get_contents、include 等)。
本套教程,以一个真实的学校教学管理系统为案例,手把手教会您如何在一张白纸上,从零开始,一步一步的用ThinkPHP5框架快速开发出一个商业项目,让您快速入门TP5项目开发。
例如:
call_user_func('file_get_contents', $_GET['path']); // 可读任意文件✅ 建议:
- 所有参数按业务类型校验:路径需
realpath()+ 白名单目录限制; - 数值参数强制
(int)或filter_var(..., FILTER_VALIDATE_INT); - 字符串参数用
htmlspecialchars()或正则限制字符集; - 数组参数检查键名、长度、嵌套深度,防止
call_user_func_array配合assert绕过。
留意 call_user_func_array 的数组陷阱
它接受参数数组,比 call_user_func 更灵活,也更易被利用:
call_user_func_array($_GET['func'], $_GET['params']);
攻击者可传:
?func=assert¶ms[]=phpinfo()
或更隐蔽地传 params[][0]=phpinfo 触发解析。
✅ 防御同上:callback 白名单 + params 结构强校验。
额外注意:$_GET['params'] 是数组时,要确认它确实是「扁平一维数组」,而非嵌套或含引用。
不复杂但容易忽略。核心就一条:所有进入 callback 位置的值,必须来自你完全掌控的静态列表;所有后续参数,必须按用途做针对性过滤。

















