如何在节点中为shell命令转义字符串?

Mac*_*iek 40 javascript shell v8 escaping node.js

nodejs中,执行外部命令的唯一方法是通过sys.exec(cmd).我想调用外部命令并通过stdin给它数据.在nodejs中,似乎还没有一种方法可以打开命令然后将数据推送到它(只是执行并接收其标准+错误输出),所以看来我现在必须这样做的唯一方法是通过单个字符串命令,例如:

var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");
Run Code Online (Sandbox Code Playgroud)

像这样的问题的大多数答案都集中在nodegs(使用谷歌的V8 Javascript引擎)或其他语言(如Python)的本机功能的正则表达式.

我想逃避dangerStr,以便像上面那样组成一个exec字符串是安全的.如果有帮助,dangerStr将包含JSON数据.

小智 36

这是我使用的:

var escapeShell = function(cmd) {
  return '"'+cmd.replace(/(["\s'$`\\])/g,'\\$1')+'"';
};
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是对命令注入和奇怪的边缘情况问题的邀请。 (5认同)
  • 这似乎是错误的:它将`foo bar`转换为``foo\bar``,它将被解析为`foo\bar`. (4认同)

Ale*_*ich 17

如果您需要简单的解决方案,您可以使用:

function escapeShellArg (arg) {
    return `'${arg.replace(/'/g, `'\\''`)}'`;
}
Run Code Online (Sandbox Code Playgroud)

因此,克里斯·约翰森提到,你的字符串只会被单引号转义.

echo 'John'\''s phone';
Run Code Online (Sandbox Code Playgroud)

它的工作原理bash是因为强烈的报价,感觉像它也适用于fish,但不会在工作zshsh.

如果您有bash可以运行脚本shzsh与之一起运行脚本'bash -c \'' + escape('all-the-rest-escaped') + '\''.

但实际上...... node.js会为你逃避所有需要的角色:

var child = require('child_process')
  .spawn('echo', ['`echo 1`;"echo $SSH_TTY;\'\\0{0..5}']);

child.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

child.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});
Run Code Online (Sandbox Code Playgroud)

这段代码将执行:

echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'
Run Code Online (Sandbox Code Playgroud)

并将输出:

stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}
Run Code Online (Sandbox Code Playgroud)

或者一些错误.

看看http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

顺便说一句,运行一堆命令的简单方法是:

require('child_process')
  .spawn('sh', ['-c', [
    'cd all/your/commands',
    'ls here',
    'echo "and even" > more'
  ].join('; ')]);
Run Code Online (Sandbox Code Playgroud)

祝你今天愉快!


Wil*_*son 8

永远不应依赖于转义进入shell参数的未知输入 -几乎总是会出现一些您从未想到过的极端情况,即允许用户在服务器上执行任意代码。

Node支持调用命令并分别传递每个参数,而无需转义。这是最安全的方法:

const { spawn } = require('child_process');
// Note that the arguments are in an array, not using string interpolation
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.log(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});
Run Code Online (Sandbox Code Playgroud)

文档在这里


Gab*_*eim 8

我赞同Will的意见,只要有可能,你就应该避免用手逃跑,而更喜欢重生。

但是,在转义不可避免的情况下,例如,如果您需要使用 exec 或者通过 ssh 执行命令。然后你可以使用base64将安全字符传递给bash并依靠bash来逃避未知。

const dangerStr = 'bad stuff here'
// base64 has safe characters [A-Za-z=0-9+/]
const dangerBase64 = btoa(dangerStr)

sys.exec(`echo "$(echo ${dangerBase64} | base64 -d)" | somecommand`)
Run Code Online (Sandbox Code Playgroud)

解释如下:

dangerBase64未知,但它不包含bash中的不安全字符。这样echo ${dangerBase64}就会输出我们想要的东西。

最后,双引号$(echo ${dangerBase64} | base64 -d)转义了用户在 bash 中传递的实际值,这是安全的并且具有与用户想要的相同的值。


Mat*_*ley -12

有一种方法可以写入外部命令:(process.createChildProcess文档返回一个带有方法的对象writecreateChildProcess但不太方便,因为它不缓冲 stdout 和 stderr,因此您将需要事件处理程序来读取块中的输出。

var stdout = "", stderr = "";
var child = process.createChildProcess("someCommand");

child.addListener("output", function (data) {
    if (data !== null) {
        stdout += data;
    }
});
child.addListener("error", function (data) {
    if (data !== null) {
        stderr += data;
    }
});
child.addListener("exit", function (code) {
    if (code === 0) {
        sys.puts(stdout);
    }
    else {
        // error
    }
});

child.write("This goes to someCommand's stdin.");
Run Code Online (Sandbox Code Playgroud)

  • 您没有解释任何有关逃避争论的事情。 (55认同)
  • 它没有回答标题中的问题,但这通过完全避免问题来解决问题。OP接受了答案,所以显然它有帮助。 (4认同)
  • 这并没有回答OP提出的问题,即逃避争论。“echo”的使用只是一个例子。OP 不仅仅是尝试将字符串传递给命令。他们试图在命令行中使用任意字符串。 (3认同)