Node.JS将eval输出重定向到字符串

时间:2017-05-19 14:32:21

标签: node.js

我可以将nodejs eval输出重定向到字符串或任何其他方式吗?

编辑:我需要将其作为字符串返回给我的Web应用程序的响应(发布数据)

var http = require('http');

var server = http.createServer(function(req, res) {
    var script = '';
    if (req.method === 'POST' && req.url === '/doeval') {
      req.on('data', function(chunk) {
        script += chunk;
      });

      req.on('end', function() {
        // script = 'console.log("aaaa")';

        var result = eval(script);
        res.write(String(result));
        res.end();
      });
    }
  }
}

server.listen(80);

输出:

result: undefined

预期:

result: aaaa

2 个答案:

答案 0 :(得分:3)

注意:运行不受信任的代码非常危险,您应该非常小心选择任何解决方案。这里提出的那个也有缺陷。为了更安全,请考虑使用容器,例如dockerlxc

在这种情况下不要使用eval。这是工作的错误工具。您真的希望在您的网络服务器上下文中eval使用任意代码吗?如果发送的代码为throw new Error('haha')process.exit(),该怎么办?这是一个巨大的安全风险!将代码写入临时文件,生成执行文件并读取其输出的节点进程。请参阅https://nodejs.org/api/child_process.html

示例:

var http = require('http');
var fs = require('fs');
var childProcess = require('child_process');

var server = http.createServer(function(req, res) {
    var script = '';
    if (req.method === 'POST' && req.url === '/doeval') {
      req.on('data', function(chunk) {
        script += chunk;
      });

      req.on('end', function() {
        var tempFileName = Date.now() + '.js';
        fs.writeFile(tempFileName, script, function(err) {
          if (err) {
            // handle error
          }

          child_process.execFile('node', [tempFileName], function(err, stdout, stderr) {
            if (err) {
              // handle err
            }

            // Delete tempfile...

            res.write(stdout);
            res.end();
          });
        });
      });
    }
  }
}

server.listen(80);

现有的npm包有助于创建和清理临时文件。另请参阅child_process的其他方法。

但是,这还不安全,因为子进程将以与服务器相同的权限运行(通过它的外观运行为root: - /)。

您应该将文件和子流程的所有者(和组)设置为nobody或其他基本上没有任何权限访问任何内容的系统用户。 Or chroot the subprocess

答案 1 :(得分:1)

注意:它被低估了但是在得出结论之前阅读了整个答案

首先,这看起来像是一个完全不安全的功能,可能会使您的系统无法打开无数漏洞。但这是一个有趣的问题,所以我会回答你如何做你所要求的,但我强烈建议你重新考虑你的要求。

话说回来,你可以将假的console对象传递给你评估的脚本,方法是将它包装在一个闭包中,就像模块在需要时被包装一样。

您可以使用vm模块在​​没有访问文件系统的单独V8上下文中运行脚本,甚至require(),而不是eval。

请注意我不建议将其保存在文件中并将其作为子进程运行,因为脚本可以访问您的文件系统,这是一个严重的漏洞。我认为将不受信任的代码作为独立进程运行的唯一选择是在无法访问网络的容器内或该容器外部的任何共享存储。

使用eval

例如:

const util = require('util');

const script = 'console.log("aaaa");';

let result = '';
const cons = {
  log: (...args) => result += (util.format(...args) + '\n'),
};
eval(`((console) => { ${script} })`)(cons);

console.log('result:', result);

如果一切都是同步的,这将有效。如果console.log以异步方式发生,那么您将不得不添加一些等待更改的方法。

所以这将工作:

const util = require('util');

const script = 'setTimeout(() => console.log("aaaa"), 1000);';

let result = '';
const cons = {
  log: (...args) => result += (util.format(...args) + '\n'),
};
eval(`((console) => { ${script} })`)(cons);

console.log('result:', result);

但这会:

const util = require('util');

const script = 'setTimeout(() => console.log("aaaa"), 1000);';

let result = '';
const cons = {
  log: (...args) => result += (util.format(...args) + '\n'),
};
eval(`((console) => { ${script} })`)(cons);

setTimeout(() => console.log('result:', result), 1500);

因为它在检查收集的输出之前等待的时间长于评估的代码创建输出所需的时间。

没有eval

您可以在无法访问其他模块,文件系统等的单独V8上下文中运行该代码。例如:

const vm = require('vm');
const util = require('util');

const script = 'console.log("aaaa");';

let result = '';

const cons = {
  log: (...args) => result += (util.format(...args) + '\n'),
};
const context = vm.createContext({console: cons});

vm.runInContext(script, context);

console.log('result:', result);

处理语法错误

您可以处理语法错误,以确保此脚本不会像这样崩溃您的应用程序:

const vm = require('vm');
const util = require('util');

const script = 'console.lo("aaaa");';

let result = '';

const cons = {
  log: (...args) => result += (util.format(...args) + '\n'),
};
const context = vm.createContext({console: cons});

try {
  vm.runInContext(script, context);
  console.log('result:', result);
} catch (err) {
  console.log('error:', err.message);
}

现在不会崩溃而是打印:

error: console.lo is not a function