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

laximzn5  于 2023-04-21  发布在  Shell
关注(0)|答案(8)|浏览(226)

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

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

对于这类问题的大多数答案都集中在正则表达式上,它在nodejs(使用Google的V8 Javascript引擎)中对我不起作用,或者来自其他语言(如Python)的本地功能。
我想跳出dangerStr,这样就可以安全地编写一个类似上面的exec字符串。如果有帮助,dangerStr将包含JSON数据。

eivgtgni

eivgtgni1#

这就是我使用的:

var escapeShell = function(cmd) {
  return '"'+cmd.replace(/(["'$`\\])/g,'\\$1')+'"';
};
ntjbwcob

ntjbwcob2#

几乎总是会有一些你没有想到的边缘情况,允许用户在你的服务器上执行任意代码。
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}`);
});

文档在这里

btqmn9zl

btqmn9zl3#

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

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

祝你今天愉快!

yacmzcpb

yacmzcpb4#

我赞同Will的观点,只要有可能,你就应该避免用手逃跑,而更喜欢产卵。
但是,如果转义是不可避免的,例如如果你需要使用exec或者你是executing a command through 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中传递的实际值,这是安全的,并且与用户想要的值相同。

gblwokeq

gblwokeq5#

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

str = JSON.stringify(str)
    .replace(/^"|"$/g,'') //remove JSON-string double quotes
    .replace(/'/g, '\'"\'"\'') //escape single quotes the ugly bash way
  • 这里假设你使用Bash的强引号(通过单引号),并且接收者可以理解JSON的类C转义。
pzfprimi

pzfprimi6#

如果您正在构建自己的软件,则可以将命令编码为base64或十六进制格式,然后从程序中解码参数。
对于我使用的Nodejs应用程序。

var base64_encode = exports.base64_encode = function(non_base64_string){
    return Buffer.from(non_base64_string).toString('base64');
}

var base64_decode = exports.base64_decode = function(base64_string){
    return Buffer.from(base64_string, 'base64').toString('ascii')
}

所以当我运行这样一个base64编码的命令时

webman grep --search "aW5jbHVkZV9vbmNlICRfU0VSVkVSWyJET0NVTUVOVF9ST09UIl0uIi9zZXR0aW5ncy5waHAiOw==" --replacement "JGRvY3VtZW50X3Jvb3QgPSBfX0RJUl9fO3doaWxlKHRydWUpe2lmIChmaWxlX2V4aXN0cygkZG9jdW1lbnRfcm9vdC4iL3NldHRpbmdzLmpzb24iKSl7YnJlYWs7fWVsc2V7JGRvY3VtZW50X3Jvb3Q9ZGlybmFtZSgkZG9jdW1lbnRfcm9vdCk7fX08bmV3bGluZT5pbmNsdWRlX29uY2UgJGRvY3VtZW50X3Jvb3QuIi9zZXR0aW5ncy5waHAiOw=="

我可以使用base64_decode轻松地获取参数searchreplacement参数

o2rvlv0m

o2rvlv0m7#

一个使用await的快速示例,具有适当的自动转义(没有手工的正则表达式)...

import util from 'util';
import {execFile as execFileWithCallback} from 'child_process';
const execFile = util.promisify(execFileWithCallback)

async function go() {
    const dangerStr1 = `"!£'\"`
    const dangerStr2 = `>> more\n ; | ^Craziness`
    const result = await execFile("echo", [dangerStr1, dangerStr2])
    console.log(result.stdout)
}
go()

/*

Will pump out...

"!£'" >> more
 ; | ^Craziness

*/

注意,你不能用这种技术来做管道,你必须把result.stdout放到另一个execFile()调用中。

1aaf6o9v

1aaf6o9v8#

有一种方法可以写入外部命令:process.createChildProcessdocumentation)返回一个带有write方法的对象。但是createChildProcess不那么方便,因为它不缓冲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.");

相关问题