首页 > 解决方案 > 在 PHP 中使用参数向量调用程序

问题描述

PHP 中用于执行外部程序的函数将单个整体命令字符串作为其参数。稍微不关心转义参数字符串可能会导致命令注入。

PHP 7.4 中增加了参数向量功能proc_open,对于普通使用来说过于复杂。那么,有没有更简单的方法来调用带有参数向量的外部程序?

标签: phpcommand-line-argumentscode-injection

解决方案


此方法仅限于在 POSIX 系统上运行的 PHP

诸如system,之类的函数popen,无论是在 PHP 还是 C 和 POSIX 的上下文中,都不意味着采用合成的命令字符串。所以设计这样一个函数的思考过程应该从一个常数值的命令字符串开始。

接下来,PHP 确实提供了像 C 一样设置单个环境变量的能力(尽管之后不会取消设置它们),并且这种能力在很大程度上不受与注入相关的攻击的影响。所以我们可以构建一个带有环境变量的命令,组装一个命令向量,然后调用execshell 内置命令。

这是完整的代码:

<?php
 $spawncmd = <<<'EOF'
set --
n=0
while [ $n -lt $execargc ] ; do
    eval "set -- \"\$@\" \"\$execarg$n\""
    unset -v execarg$n
    n=$((n+1))
done
unset -v execargc
exec "$@"
EOF;

 // Invokes external program and return its output.
 function spawn($args)
 {
   global $spawncmd;
   
   putenv("execargc=".count($args));
   for($i=0; $i<count($args); $i++)
     putenv("execarg$i=".$args[$i]);

   $ret = shell_exec($spawncmd);
   
   putenv("execargc=");
   for($i=0; $i<count($args); $i++)
     putenv("execarg$i=");

   return $ret;
 }

 // Invokes external program and return its exit status.
 function catspawn($args)
 {
   global $spawncmd;
   
   putenv("execargc=".count($args));
   for($i=0; $i<count($args); $i++)
     putenv("execarg$i=".$args[$i]);

   $ret = null;
   passthru($spawncmd, $ret);
   
   putenv("execargc=");
   for($i=0; $i<count($args); $i++)
     putenv("execarg$i=");

   return $ret;
 }

 // Argument vector version of popen.
 function pspawn($args, $mode)
 {
   global $spawncmd;
   
   putenv("execargc=".count($args));
   for($i=0; $i<count($args); $i++)
     putenv("execarg$i=".$args[$i]);

   $ret = popen($spawncmd, $mode);
   
   putenv("execargc=");
   for($i=0; $i<count($args); $i++)
     putenv("execarg$i=");

   return $ret;
 }

推荐阅读