【问题标题】:How to split command by & && || ; and | in php that will work with quotes如何用 & && || 分割命令;和 |在 php 中可以使用引号
【发布时间】:2017-04-28 07:36:01
【问题描述】:

我有这个函数来验证 php 中的命令?

public function validate_command($command) {
    if (isset($this->config->settings->guest_commands)) {
        $commands = $this->config->settings->guest_commands;
    } else {
        $commands = array('echo', 'cat', 'ls', 'find', 'cd', 'grep', 'test', 'xargs');
    }
    $cmd_re = "(" . implode("|", array_diff($commands, array("xargs"))) . ")";
    if (in_array("xargs", $commands)) {
        $re = "/^\s*($cmd_re|xargs\s*$cmd_re)/";
    } else {
        $re = "/^\s*$cmd_re/";
    }
    $separators = "/(&&|\|\||\||;)/";
    $parts = preg_split($separators, $command, null, PREG_SPLIT_DELIM_CAPTURE);
    $result = array();
    foreach ($parts as $part) {
        if (!preg_match($re, trim($part)) && !preg_match($separators, $part)) {
            $last = array_pop($commands);
            $message = "guest user can only execute: " .
                     implode(", ", $commands) . " and " . $last;
            throw new Exception($message);
        } else if (preg_match('/(>|`|\$\()/', $part)) {
            throw new Exception("guest user can't use redirect to write to files" .
                                " or execute subshell");
        } else {
            $result[] = $part;
        }
    }
    return implode($result);
}

它使用以下命令拆分命令:

    $separators = "/(&&|\|\||\||;)/";
    $parts = preg_split($separators, $command, null, PREG_SPLIT_DELIM_CAPTURE);

如何使它适用于echo "©" 之类的命令?它应该返回相同的命令,但会抛出异常。

我可以在后面使用单个正则表达式吗?它应该是什么样子?它应该适用于这样的情况:

echo "©\"©" && echo "©"

$parts 应该有array('echo "©\"©"', '&&', 'echo "©"')(可以包含 && 或命令周围的空格)

我尝试了$separators = "/(?<![\"'](?:[^\"']|\\[\"'])*)(&&|\|\||\||;)/";,但出现异常:

preg_split(): Compilation failed: lookbehind assertion is not fixed length at offset 24

遍历字符串是唯一的选择吗?

【问题讨论】:

  • 那个 preg_split() 错误是因为这个 [\"'])* 部分在后视。后面是zero length assertions。你不能给它们添加量词。
  • 您想将© 视为& 吗?那就换吧。或者将©© 添加到正则表达式模式中。
  • @WiktorStribiżew 想要忽略拆分正则表达式,如果它在单引号或双引号中。
  • 所以,匹配它们并跳过 - 在你的正则表达式前面加上 (?:"[^"]*"|'[^']*')(*SKIP)(*F)|
  • @WiktorStribiżew 它有效,你可以添加一个答案,但我用过"/1(?:\"(?:[^\"]|\\\")*\"|'(?:[^']|\\')*')(*SKIP)(*F)|(&&|\|\||\||;)/"

标签: php regex preg-split


【解决方案1】:

您应该匹配引号之间的所有内容,然后将它们从结果集中排除:

$re = <<< 'RE'
~(?:"[^"\\]*(\\.[^"\\]*)*"|'[^'\\]*(\\.[^'\\]*)*')\K|(&&|\|\||\||;)~
RE;

$str = <<< 'STR'
echo '"&co\"py;"'
STR;

var_dump(preg_split($re, $str, -1, PREG_SPLIT_NO_EMPTY));

输出:

array(1) {
  [0]=>
  string(17) "echo '"&co\"py;"'"
}

【讨论】:

  • 什么是正则表达式中的\K
  • 它强制引擎重置与输出相匹配的部分字符串。见\K match resetter
  • 我应该提一下 (&amp;&amp;|\|\||\||;) 可以减少到 (&amp;&amp;|\|+|;) 如果你知道三个或更多的连续管道将不可能进入命令。
  • 我不知道它应该如何与||| 一起工作。我认为最好只使用\|{1,2}
  • 它返回错误的结果为 sting echo "&amp;copy;\"&amp;;" "&amp;&amp; rm" &amp;&amp; echo ls
猜你喜欢
  • 2023-03-03
  • 2022-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-13
  • 2010-11-18
  • 2011-03-26
相关资源
最近更新 更多