我想使用标准的JavaScript解析用户表达式,以验证布尔值,例如:
var1 > obj1.prop1 && var2 + 1 <= 5
由于这些表达式是由用户编写的,因此我希望它们是干净的,因为它们将在 NodeJS 服务器端进行评估。
是否有一种方法可以利用 Node 的强大功能直接评估表达式,而不必像解析文本查找模式和重新发明轮子那样存在代码注入的风险?
我想使用标准的JavaScript解析用户表达式,以验证布尔值,例如:
var1 > obj1.prop1 && var2 + 1 <= 5
由于这些表达式是由用户编写的,因此我希望它们是干净的,因为它们将在 NodeJS 服务器端进行评估。
是否有一种方法可以利用 Node 的强大功能直接评估表达式,而不必像解析文本查找模式和重新发明轮子那样存在代码注入的风险?
也许这不是你想听的答案。但是你必须付出努力。没有什么捷径。
你的问题自相矛盾,要求“标准JavaScript”又要“避免代码注入的风险”。这两者不能兼得。标准JavaScript允许像'require("fs").rmdirSync("/")'
这样的表达式。
用户输入的表达式必须被约束为严格受限的JavaScript子集。服务器必须验证输入是否局限于此子集,然后再尝试对其进行评估。
因此,首先需要仔细考虑允许的受限子集是什么。看起来你希望允许常量整数,如'5'、运算符如'>'、'&&'和'<='。还允许访问变量,如'var1'、'obj1.prop1'、'var2'。我想你需要非常具体地列出允许使用的变量列表。
防止脚本注入的关键在于定义一个只包含你知道是安全的内容的子集。你不应该尝试从整个JavaScript开始,并排除你认为危险的内容 - 因为你会遗漏一些。
一旦你仔细定义了表达式可能包含的内容,你需要实现代码来解析和验证表达式。你可以找到一个库或标准代码来完成这个任务,但你必须修改或配置它以符合你具体的需求。
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在答案中建议的那样,您应该采用白名单某些功能而不是黑名单有害功能的方式。