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

IT技术 javascript shell escaping node.js v8
2021-03-11 19:20:13

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

var dangerStr = "bad stuff here";
sys.exec("echo '" + dangerStr + "' | somecommand");

大多数此类问题的答案都集中在 nodejs(使用 Google 的 V8 Javascript 引擎)中对我不起作用的正则表达式或 Python 等其他语言的本机功能。

我想逃避dangerstr,这样就可以安全地编写像上面那样的exec字符串。如果有帮助,dangerStr 将包含 JSON 数据。

6个回答

这是我使用的:

var escapeShell = function(cmd) {
  return '"'+cmd.replace(/(["'$`\\])/g,'\\$1')+'"';
};
@DavidTorresabc$abc成为abc\$abc
2021-05-01 19:20:13
这似乎是错误的:它转义foo bar"foo\ bar",它将被解析为foo\ bar
2021-05-08 19:20:13
这似乎是对命令注入和奇怪的边缘案例问题的邀请。
2021-05-13 19:20:13
\s如果将所有内容都用双引号括起来,则不需要引用空格 ( ),因为这会自动保留所有空格。如果字符串周围没有引号,您只需要这样做,否则 shell 会将空格视为参数分隔符。此外,当您将制表符转换为时,您没有正确引用空格,\ 但正确的形式是\t.
2021-05-14 19:20:13
$如果使用撇号,则无需逃避'
2021-05-17 19:20:13

如果您需要简单(但正确)的解决方案,您可以使用:

function escapeShellArg (arg) {
    return `'${arg.replace(/'/g, `'\\''`)}'`;
}

所以你的字符串将像 Chris Johnsen 提到的那样简单地用单引号转义。

echo 'John'\''s phone';

它的工作原理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);
});

此代码块将执行:

echo '`echo 1`;"echo $SSH_TTY;'\''\\0{0..5}'

并将输出:

stdout: `echo 1`;"echo $SSH_TTY;\'\\0{0..5}

或一些错误。

看看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('; ')]);

祝你今天过得愉快!

永远不应该依赖转义到 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}`);
});

文档在这里

我支持Will的意见,只要有可能,你应该避免手动逃逸,而更喜欢 spawn。

但是,在转义不可避免的情况下,例如如果您需要使用 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`)

解释如下:

dangerBase64未知,但它在bash中不包含不安全的字符因此echo ${dangerBase64}将输出我们想要的。

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

如果您还需要处理特殊字符(换行符等),您可以这样做:

str = JSON.stringify(str)
    .replace(/^"|"$/g,'') //remove JSON-string double quotes
    .replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way

这假设您通过单引号使用 Bash 的强引用)并且接收者可以理解 JSON 的类 C 转义。