如何安全地评估用户输入的表达式

4

我想使用标准的JavaScript解析用户表达式,以验证布尔值,例如:

var1 > obj1.prop1 && var2 + 1 <= 5

由于这些表达式是由用户编写的,因此我希望它们是干净的,因为它们将在 NodeJS 服务器端进行评估。

是否有一种方法可以利用 Node 的强大功能直接评估表达式,而不必像解析文本查找模式和重新发明轮子那样存在代码注入的风险?


1
这有帮助吗?http://jsep.from.so/ - Ilaya Raja S
@IlayaRajaS 很酷的库!但是它不会评估表达式,至少我找不出来。 - DomingoSL
你在 Linux 服务器上运行 Node.js 吗? - t.niese
3个回答

4

也许这不是你想听的答案。但是你必须付出努力。没有什么捷径。

你的问题自相矛盾,要求“标准JavaScript”又要“避免代码注入的风险”。这两者不能兼得。标准JavaScript允许像'require("fs").rmdirSync("/")'这样的表达式。

用户输入的表达式必须被约束为严格受限的JavaScript子集。服务器必须验证输入是否局限于此子集,然后再尝试对其进行评估。

因此,首先需要仔细考虑允许的受限子集是什么。看起来你希望允许常量整数,如'5'、运算符如'>'、'&&'和'<='。还允许访问变量,如'var1'、'obj1.prop1'、'var2'。我想你需要非常具体地列出允许使用的变量列表。

防止脚本注入的关键在于定义一个只包含你知道是安全的内容的子集。你不应该尝试从整个JavaScript开始,并排除你认为危险的内容 - 因为你会遗漏一些。

一旦你仔细定义了表达式可能包含的内容,你需要实现代码来解析和验证表达式。你可以找到一个库或标准代码来完成这个任务,但你必须修改或配置它以符合你具体的需求。


1

0
通常无论你最终使用什么,我建议启动一个评估过程,在其中放弃所有特权,并仅使用Unix域套接字进行通信。然后将要评估的代码发送给它。 以root身份启动它并打开Unix域套接字,然后将特权降低到nobody
process.setgid('nobody');
process.setuid('nobody');

有一件事情你应该避免做,就是像这样:

const root = global = {};
const require = function() {
  console.log('tryed to call require', arguments);
}

eval("require('fs')");

这个方法看起来可能有效,但是例如在ES6中引入了import关键字,因此即使覆盖了require,仍然可以使用import来加载模块。

另外,像vm.runInContext('globalVar *= 2;', sandbox);这样的方法,如Safely sandbox and execute user submitted JavaScript?所述,也无法帮助。但是参考sandcastle,可能是您可以考虑的东西,但即使使用沙箱库,我仍建议在隔离的非特权进程中运行它。

正如James在答案中建议的那样,您应该采用白名单某些功能而不是黑名单有害功能的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,