PHP命令执行风险自查核心是定位所有调用exec、system、shell_exec、passthru、popen、proc_open及反引号的代码点,重点核查是否将$_GET、$_POST等不可信输入直接拼入命令;必须使用escapeshellarg()、白名单校验或内置函数替代,禁用高危函数并限制运行权限。

PHP命令执行风险自查,核心是定位所有可能调用系统命令的代码点,并验证是否对用户输入做了足够防护。重点不是“有没有用这些函数”,而是“有没有让不可信输入直接参与命令构造”。
快速定位危险函数调用
在项目中全局搜索以下函数名,不区分大小写:
- exec、system、shell_exec、passthru
-
popen、proc_open、反引号(
`command`) - 间接调用:如 call_user_func 或 call_user_func_array 的第一个参数可控,且实际执行的是上述函数
特别注意配置文件、模板文件、第三方类库(如旧版ImageMagick封装、PDF生成工具)中隐藏的调用。某些框架扩展或CMS插件也会在后台静默调用shell命令。
检查输入是否被直接拼接
找到调用点后,逐行确认命令字符串是否包含任何来自外部的数据源,包括:
立即学习“PHP免费学习笔记(深入)”;
- $_GET、$_POST、$_REQUEST、$_COOKIE、$_SERVER['HTTP_*']
- 数据库读取的字段(如用户自定义脚本路径、配置项)
- 文件内容(如读取上传的配置文件后拼入命令)
典型危险写法示例:$ip = $_GET['ip']; system("ping -c 1 " . $ip);
即使只拼接一个参数,只要没过滤,就存在注入可能(如传入 127.0.0.1; rm -f /tmp/*)。
验证是否做了有效过滤与转义
仅用 trim()、intval() 或正则简单替换(如删掉分号)远远不够。真正有效的防护应满足以下至少一项:
- 使用 escapeshellarg() 包裹每个外部参数(适用于单个字符串参数,如文件名、用户名)
- 使用 escapeshellcmd() 处理整条命令(但不能替代 escapeshellarg,且对管道符等支持有限)
- 采用白名单机制:只允许预设的固定值(如
['start', 'stop', 'restart']),用 in_array() 校验 - 完全避免拼接:改用数组形式传参(proc_open 支持)、或改用PHP原生函数实现相同功能(如用 file_get_contents 替代 cat,用 getimagesize 替代 identify)
确认运行权限与函数禁用状态
即使代码逻辑安全,底层环境仍可能放大风险:
- 检查 php.ini 中是否禁用了高危函数:
disable_functions = exec,system,shell_exec,passthru,popen,proc_open - 确认Web服务器进程(如 www-data、nginx)运行权限是否最小化——不应拥有写入系统目录、访问数据库配置文件等权限
- 查看是否启用 open_basedir 限制脚本可访问的文件系统路径
- 生产环境务必关闭 display_errors,防止错误信息泄露命令路径或结构



















