本文内容参考自《PHP安全之道》。
PHP提供代码执行(code execution)类函数是为了方便开发人员处理各类数据,但是不合理的使用会造成潜在的安全风险。其中的eval, assert, pre_replace, create_function等能执行代码的函数, 不会对用户输入的参数进行过滤,可能导致被执行任意恶意代码。
一、代码执行函数
eval()
像js一样, php的eval()函数可以把字符串当做php代码执行,通常用于处理模板和动态加载php代码。
eval('phpinfo()');
assert()
assert()函数用来检查一个断言(表达式)是否为 FALSE, 主要用来进行debug。
// PHP5:
assert( mixed $assertion[, string $description] ) : bool
// PHP7:
assert( mixed $assertion[, Throwable $exception] ) : bool
如果 $assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行:
assert('phpinfo()');
preg_replace()
在preg_repalce函数中, 当第一个参数的正则表达式中有e修正符时,第二个参数的字符串被当做php代码执行。
preg_replace('/pregStr/e', 'phpinfo()', 'subject');
从PHP 5.5.0 起, 传入 "\e" 修饰符的时候,会产生一个 E_DEPRECATED 错误; PHP 7.0.0 起,会产生 E_WARNING 错误,同时 "\e" 也无法起效。
PHP5.6:
PHP Warning: preg_replace(): The /e modifier is no longer supported, use preg_replace_callback instead in ...
Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in ...
从PHP7.0开始i:
Warning: preg_replace(): The /e modifier is no longer supported, use preg_replace_callback instead in ...
create_function()
create_function的作用是从传递的参数创建匿名函数,并返回唯一的名称。该函数与eval作用相同, 并且与eval有同样的安全问题。并且性能和内存使用差的特点。
$fun_new = create_function('$a, $b', ';} phpinfo();/*');
从PHP 5.3.0以后,用原生的匿名函数(anonymous function, 或者叫闭包函数 Closure function)取而代之。 但是在截止到目前的PHP7.4.6版本中仍然支持该函数, 只是会报一个"Warning: Unterminated comment starting line 1"。
容易导致安全问题的函数还有很多, 除了上面提到的eval, assert, preg_replace, create_fucntion, 还有: 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, escapeshellcmd, exec, include, include_once, ob_start, passthru, pcntl_exec, requiere, require_once, register_shutdown_function, register_tick_function, set_error_handler, shell_exec, stram_filter_register, system, usort, uasort, uksort,以及xml操作函数等。
二、代码执行防御
escapeshellarg()、escapeshellcmd()函数用来保证传入的命令执行函数里的参数确实是以字符串的形式存在,不能被注入。
escapeshellarg() 函数将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含 exec()、system()、执行运算符 。
$dir = '/usr/; rm *';
var_dump('ls '.escapeshellarg($dir)); //结果是: string(16) "ls "/usr/; rm *""
system('ls '.escapeshellarg($dir));
escapeshellcmd() 函数对字符串中可能会欺骗shell命令执行任意命令的字符进行转义,保证用户输入的数据在传送到exec()或system()或执行操作符之前进行转义。
反斜线(\)会在以下字符之前插入: `|*?~<>^()[]{}$, \x0A 和 \xFF。单引号和双引号 仅在不配对儿的时候被转义。在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。
$command = './configure '.$_POST['configure_options'];
$escaped_command = escapeshellcmd($cmd);
system($escaped_command);
escapeshellcmd() 应被用在完整的命令字符串上。即使如此,攻击者还是可以传入任意数量的参数。请使用 escapeshellarg() 函数对单个参数进行转义。
如果觉得博客文章对您有帮助,异或土豪有钱任性,可以通过以下扫码向我捐助。也可以动动手指,帮我分享和传播。您的肯定,是我不懈努力的动力!感谢各位亲~