这只是前端,而不是后端。我也承认这是一个坏主意。此时我只是好奇。
我有一张记录表。我希望用户能够输入JavaScript条件语句,然后将其应用于表以过滤记录。
例如,要过滤掉名称少于6个字符的记录,我可以输入:
record.name.length < 6
不使用外部库,我发现这样做的最简单方法是使用eval
。但是,在使用eval
时,我当然会介绍用户破解代码的风险(不是一个大问题,因为这只是前端,但仍然是用户体验问题)。
我想清理用户输入,以便它不能更改任何值。到目前为止,我认为我只需要做这两件事就可以使eval
“安全”:
=
有了这两个项目,我还需要做些什么来阻止用户输入更改值吗?
答案 0 :(得分:4)
比eval
更安全的一种方法是使用the Function
constructor。据我所知,这个答案是完全安全的,但很可能有一些我不知道或已经忘记的警告,所以每个人都可以随时回答我的回答错。
Function
构造函数允许您从其字符串和参数名称列表构造函数。例如,函数
function(x, y) {
return x + y;
}
可以写成
new Function('x', 'y', 'return x + y;')
或只是
Function('x', 'y', 'return x + y;')
请注意,尽管函数体可以访问函数定义中声明的变量,但它无法从调用Function
构造函数的本地作用域访问变量;在这方面,它比eval
更安全。
例外是全局变量;函数体可以访问它们。也许您希望其中一些可以访问;对于他们中的许多人来说,你可能不会。但是,有一种方法:将全局变量的名称声明为函数的参数,然后调用函数用伪值覆盖它们。例如,请注意此表达式返回全局Object
:
(function() { return Object; })()
但是这个会返回'not Object'
:
(function(Object) { return Object; })('not Object')
因此,要创建一个无法访问任何全局变量的函数,您所要做的就是在javascript字符串上调用Function
构造函数,并使用以所有全局变量命名的参数,然后调用函数对所有全局变量都有一些无害的值。
当然,有些变量(例如record
)你确实希望javascript代码能够访问。 Function
的参数名称参数也可以用于此。我假设您有一个名为myArguments
的对象,其中包含它们,例如:
var myArguments = {
record: record
};
(顺便说一句,不要将其称为arguments
,因为它是一个保留字。)现在我们需要函数参数的名称列表。有两种:来自myArguments
的参数和我们想要覆盖的全局变量。方便地,在客户端javascript中,所有全局变量都是单个对象window
中的属性。我相信在没有原型属性的情况下使用自己的属性就足够了。
var myArgumentNames = Object.keys(myArguments);
var globalNames = Object.keys(window);
var allArgumentNames = myArgumentNames.concat(globalNames);
接下来我们想要参数的值:
var myArgumentValues = myArgumentNames.map(function(key) {
return myArguments[key];
};
我们不需要为全局变量做值部分;如果我们不这样做,他们只会被设置为undefined
。 (哦,不要做Object.keys(myArguments).map(...)
,因为数组出错的可能性很小,因为Object.keys
没有做出任何规定保证其返回值的顺序。你必须使用相同的数组myArgumentNames
。)然后调用Function
构造函数。由于Function
的参数很多,明确地列出它们是不切实际的,但我们可以使用函数上的apply方法来解决这个问题:
var myFn = Function.apply(null, allArgumentNames.concat([jsString]))
现在我们只使用apply
方法生成的参数列表调用此函数。对于此部分,请注意jsString
可能包含对this
的引用;我们希望确保this
不会帮助用户做恶意的事情。脚本中this
的值是apply
的第一个参数。实际上这并不完全正确 - 如果jsString
没有使用严格模式,那么尝试将this
设置为undefined
或null
将会失败,并且this
将成为全球对象。你可以通过强制脚本进入严格模式(使用'"use strict";\n' + jsString
),或者只是将this
设置为空对象来解决这个问题。像这样:
myFn.apply({}, myArgumentValues)