php x20,PHP: escapeshellarg - Manual

On Windows, this function naively strips special characters and replaces them with spaces. The resulting string is always safe for use with exec() etc, but the operation is not lossless - strings containing " or % will not be passed through to the child process correctly.

Correctly escaping shell commands on Windows is not a simple matter. Programs must consider two distinct escape mechanisms which serve different purposes:

1) The convention used by the CommandLineToArgV() windows system function, used by the child process to interpret the command line string

2) The convention used by cmd.exe to escape shell meta-characters (e.g. output redirection controls)

All commands should be escaped for CommandLineToArgV() - this mechanism is applied to each argument individually before it is appended to the command line string. The resulting string may be safely used with the CreateProcess() family of system functions. However...

In almost all cases when creating a child process from PHP on Windows, it is done indirectly by invoking cmd.exe - this is to enable the use of shell functionality such as I/O redirection and environment variable substitution. As a consequence, the entire command string must be further escaped for cmd.exe. If the executed command contains further indirect calls through cmd.exe, each child command must be escaped again for each level of indirection.

The following functions can be used to correctly escape strings such that they are safely passed through to a child process:

* Escape a single value in accordance with CommandLineToArgV()

* https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)

*/functionescape_win32_argv(string $value):string{

static$expr='(

[\x00-\x20\x7F"] # control chars, whitespace or double quote

| \\\\++ (?=("|$)) # backslashes followed by a quote or at the end

)ux';

if ($value==='') {

return'""';

}$quote=false;$replacer= function($match) use($value, &$quote) {

switch ($match[0][0]) {// only inspect the first byte of the matchcase'"':// double quotes are escaped and must be quoted$match[0] ='\\"';

case' ': case"\t":// spaces and tabs are ok but must be quoted$quote=true;

return$match[0];

case'\\':// matching backslashes are escaped if quotedreturn$match[0] .$match[0];

default: throw newInvalidArgumentException(sprintf("Invalid byte at offset %d: 0x%02X",strpos($value,$match[0]),ord($match[0])

));

}

};$escaped=preg_replace_callback($expr,$replacer, (string)$value);

if ($escaped===null) {

throwpreg_last_error() ===PREG_BAD_UTF8_ERROR? newInvalidArgumentException("Invalid UTF-8 string")

: newError("PCRE error: ".preg_last_error());

}

return$quote// only quote when needed?'"'.$escaped.'"':$value;

}/** Escape cmd.exe metacharacters with ^ */functionescape_win32_cmd(string $value):string{

returnpreg_replace('([()%!^"<>&|])','^$0',$value);

}/** Like shell_exec() but bypass cmd.exe */functionnoshell_exec(string $command):string{

static$descriptors= [['pipe','r'],['pipe','w'],['pipe','w']],$options= ['bypass_shell'=>true];

if (!$proc=proc_open($command,$descriptors,$pipes,null,null,$options)) {

throw new \Error('Creating child process failed');

}fclose($pipes[0]);$result=stream_get_contents($pipes[1]);fclose($pipes[1]);stream_get_contents($pipes[2]);fclose($pipes[2]);proc_close($proc);

return$result;

}// usage$badString='String with "C:\\quotes\\" or malicious %OS% stuff \\';$cmdParts= ['php','-d','display_errors=1','-d','error_reporting=-1','-r','echo $argv[1];',$badString// child process $argv[1] value];/* The typical approach - works fine on POSIX shells but totally wrong

on Windows */$wrong=implode(' ',array_map('escapeshellarg',$cmdParts));/* Always escape each argument individually */$escaped=implode(' ',array_map('escape_win32_argv',$cmdParts));/* In almost all cases, escape for cmd.exe as well - the only exception is

when using proc_open() with the bypass_shell option. cmd doesn't handle

arguments individually, so the entire command line string can be escaped,

no need to process arguments individually */$cmd=escape_win32_cmd($escaped);$cmds= ['escapeshellarg() - wrong'=>$wrong,'escape_win32_argv() - correct for bypass_shell'=>$escaped,'escape_win32_cmd(escape_win32_argv()) - correct everywhere else'=>$cmd,

];

functioncheck($original,$received)

{$match=$original===$received?'=':'X';

return"$match'$received'";

}

foreach ($cmdsas$description=>$cmd) {

echo"$description\n";

echo"$cmd\n";

echo"  original:         '$badString'\n";

echo"  shell_exec():   ".check($badString,shell_exec($cmd)) ."\n";

echo"  noshell_exec(): ".check($badString,noshell_exec($cmd)) ."\n";

echo"\n";

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值