web安全代码基础-PHP(代码/命令执行安全)

📅 2026/7/3 2:47:29 👁️ 阅读次数 📝 编程学习
web安全代码基础-PHP(代码/命令执行安全)

目录

代码执行漏洞

前置说明

eval () 代码执行漏洞

漏洞代码

攻击 Payload(URL 传参)

原理

修复方案

assert () 代码执行漏洞

漏洞代码

攻击 Payload

修复方案

preg_replace /e 修饰符 代码执行(PHP5 专属)

漏洞代码

攻击 Payload

修复方案

create_function 动态函数代码执行(PHP5)

漏洞代码

攻击payload

修复方案

容易导致代码执行的 PHP 函数

直接高危(传入可控内容可直接执行 PHP 代码 / 系统命令)

回调可控间接风险(回调参数由用户可控时,可调用任意函数触发代码执行)

无代码 / 命令执行风险

PHP 命令执行漏洞

漏洞核心原理

system () 漏洞

漏洞代码

攻击 URL Payload

exec () 漏洞

漏洞代码

攻击 URL Payload

shell_exec () / 反引号 `` 漏洞

漏洞代码

攻击 URL Payload

passthru () 漏洞

pcntl_exec () 漏洞

攻击 URL Payload

安全修复方案

escapeshellarg(最优,推荐)

输入白名单校验

php.ini 全局禁用危险命令函数


代码执行漏洞
前置说明

PHP 代码执行漏洞成因:可控用户输入直接传入可执行代码的函数,攻击者可拼接恶意 PHP 语法实现任意代码执行、读写文件、服务器提权等。

危险函数eval()assert()、旧版本preg_replace()/e修饰符、create_function()(PHP5)、call_user_func()等。

  • preg_replace /e漏洞仅PHP 5.0 ~ PHP 5.5有效,PHP5.6 + 移除/e修饰符
  • create_function()在 PHP7 废弃,PHP8 直接删除
  • assert()在 PHP7.2 后默认关闭代码执行,需手动开启assert.active=1
  • eval()全版本高危,无版本限制
eval () 代码执行漏洞
漏洞代码
<?php // 获取GET参数 cmd,用户可控输入 $cmd = $_GET['cmd']; // 直接拼接执行,存在代码执行漏洞 eval("\$ret = $cmd;"); echo "执行结果:".$ret; ?>
攻击 Payload(URL 传参)
http://127.0.0.1/vuln_eval.php?cmd=phpinfo(); http://127.0.0.1/vuln_eval.php?cmd=system('whoami'); http://127.0.0.1/vuln_eval.php?cmd=file_put_contents('shell.php','<?php eval($_POST[cmd]);?>');
原理

eval 内部会把字符串当做 PHP 代码解析,传入分号闭合原有语法后可执行任意函数。

修复方案

1、禁止使用 eval 处理用户输入

2、若必须动态运算,使用安全的表达式白名单,严格正则过滤特殊字符;(){}'"

assert () 代码执行漏洞

assert 原本用于断言判断,传入字符串会当做代码执行(PHP7.2 前默认开启)

漏洞代码
<?php $input = $_GET['a']; assert($input); ?>
攻击 Payload
http://127.0.0.1/vuln_assert.php?a=phpinfo(); http://127.0.0.1/vuln_assert.php?a=system('id');
修复方案

1、php.ini 设置assert.active=0assert.exception=1

2、不传用户可控变量给 assert

preg_replace /e 修饰符 代码执行(PHP5 专属)
漏洞代码
<?php $str = $_GET['str']; // 带 /e 修饰符,替换内容会被eval执行 preg_replace('/(\w+)/e', '$1', $str); ?>

执行逻辑:

正常情况下,preg_replace是做字符串替换。但加上/e后,流程变成了:

  1. $str中提取出每个单词(例如"hello")。

  2. $1(即"hello")放入替换字段。

  3. 由于/e的存在,PHP会尝试执行hello这个代码

攻击 Payload
http://127.0.0.1/vuln_preg.php?str={phpinfo()} http://127.0.0.1/vuln_preg.php?str={system('ls -l')}
修复方案

1、废弃/e修饰符,使用preg_replace_callback回调函数替代

2、PHP5 升级 PHP7/8

安全替代写法:

// 安全无执行风险 $res = preg_replace_callback('/(\w+)/', function($m){ return $m[1]; }, $str); //即使 $str = "phpinfo()",它也只是: //匹配到单词 phpinfo //回调函数返回字符串 "phpinfo" //最终结果是字符串 "phpinfo",绝对不会执行 phpinfo() 函数
create_function 动态函数代码执行(PHP5)

create_function 通过拼接字符串创建匿名函数,可控输入会注入恶意代码。

漏洞代码
<?php $user = $_GET['name']; // 可控输入拼接函数逻辑 $func = create_function('', "echo 'hello ".$user."';"); $func(); ?>
攻击payload
http://127.0.0.1/vuln_create.php?name=';phpinfo();//

注入后生成的函数代码变为:

function(){echo 'hello ';phpinfo();//';} //分号闭合原有语句,注释掉剩余代码,实现任意代码执行。

执行过程:

  1. create_function()动态创建一个匿名函数

  2. 函数体是字符串:"echo 'hello ".$user."';"

  3. $user变量会被直接拼接进函数体字符串中

修复方案

PHP7+ 不再推荐 create_function,改用匿名函数function(){} / fn(),禁止拼接用户输入。

容易导致代码执行的PHP函数
直接高危(传入可控内容可直接执行 PHP 代码 / 系统命令)
  • assert ():传入字符串参数会当作 PHP 代码执行,易代码注入
  • pcntl_exec ():加载程序执行系统二进制,可控参数会命令注入
  • preg_replace:PHP5 带 /e 修饰符时替换内容会 eval 执行 PHP 代码
  • create_function ():拼接用户输入生成匿名函数,可注入 PHP 代码
  • shell_exec ():执行 shell 命令并返回输出,可控参数造成命令执行
  • exec ():调用外部 shell 执行系统命令,存在命令注入风险
  • system ():执行系统命令并直接打印输出,极易命令注入
  • include/include_once/require/require_once:文件包含,可控路径可包含恶意 PHP 文件执行代码
回调可控间接风险(回调参数由用户可控时,可调用任意函数触发代码执行)

1、array_filter、array_map、array_reduce、array_diff_uassoc、array_diff_ukey、array_udiff、array_udiff_assoc、array_udiff_uassoc、array_intersect_assoc、array_intersect_uassoc、array_uintersect、array_uintersect_assoc、array_uintersect_uassoc、array_walk、array_walk_recursive、usort、uasort、uksort:数组处理自定义回调,用户能控制回调名即可调用危险函数执行代码

2、register_shutdown_function、register_tick_function、set_error_handler、stream_filter_register、ob_start:注册各类回调处理函数,回调名称可控可劫持执行任意 PHP 函数

3、xml_set_xxx 系列处理器函数:设置 XML 解析回调,回调可控可执行任意 PHP 代码

无代码 / 命令执行风险

escapeshellcmd ():仅转义 shell 特殊字符,只做过滤,本身不会执行命令

PHP 命令执行漏洞
漏洞核心原理

用户可控外部输入(GET/POST/COOKIE)直接拼接进系统命令执行函数,未做转义 / 过滤;攻击者使用; | & || && %0a等命令分隔符追加任意系统命令,实现命令注入。

system () 漏洞
漏洞代码
<?php // 接收用户传入的ip,无任何安全处理 $target = $_GET['ip']; // 可控变量直接拼接系统命令 system("ping ".$target); ?>
攻击 URL Payload
http://127.0.0.1/vuln_system.php?ip=127.0.0.1;whoami http://127.0.0.1/vuln_system.php?ip=127.0.0.1|ls http://127.0.0.1/vuln_system.php?ip=127.0.0.1&&cat /etc/passwd

效果:先执行 ping,再执行whoami/ls/cat,读取服务器敏感信息

exec () 漏洞
漏洞代码
<?php $target = $_GET['ip']; $res = []; exec("ping ".$target, $res); print_r($res); ?>
攻击 URL Payload
?vip=127.0.0.1;pwd

命令输出存入数组并打印,可读取目录、敏感文件。

shell_exec () / 反引号 `` 漏洞

二者功能完全一致,通过 shell 执行命令并返回完整输出

漏洞代码
<?php $target = $_GET['ip']; $output = shell_exec("ping ".$target); echo $output; // 等价写法 $output = `ping $target`; ?>
攻击 URL Payload
?ip=127.0.0.1;id
passthru () 漏洞

直接原始输出命令数据流,无缓存,适合读取文件、反弹 shell

<?php $target = $_GET['ip']; $handle = popen("ping ".$target, "r"); echo fgets($handle); pclose($handle); ?>
pcntl_exec () 漏洞

直接调用二进制程序,参数可控造成参数注入

<?php $param = $_GET['p']; pcntl_exec("/bin/echo", [$param]); ?>
攻击 URL Payload
?p=test;ls ?ip=127.0.0.1;echo '<?php eval($_POST[cmd]);?>' > shell.php //高危拓展利用:写入网站后门
安全修复方案
escapeshellarg(最优,推荐)

将用户输入整体封装为独立参数,所有命令分隔符自动转义失效

运行过程:

  1. 在字符串首尾添加单引号'

  2. 将字符串内的单引号'替换为'\''(结束单引号 → 转义单引号 → 开始新单引号)

<?php $target = $_GET['ip']; $safe_target = escapeshellarg($target); system("ping ".$safe_target); ?>

补充两个转义函数区别:

  • escapeshellarg ():转义单个参数,阻断命令注入,业务首选;
  • escapeshellcmd ():转义整条命令,仅适合固定命令场景,无法完全防御参数注入,不建议单独使用。
输入白名单校验

严格限制输入格式(如仅允许 IP 数字和点)

<?php $target = $_GET['ip']; if (!preg_match('/^\d{1,3}(\.\d{1,3}){3}$/', $target)) { exit("非法输入"); } system("ping ".$target); ?>

正则匹配:

部分含义说明
^开始锚点从字符串开头匹配
\d{1,3}1-3位数字匹配 0-999
(\.\d{1,3}){3}分组重复3次匹配.数字重复3次
$结束锚点匹配到字符串结尾
php.ini 全局禁用危险命令函数

disable_functions = system,exec,shell_exec,passthru,popen,pcntl_exec