我正在编写一些JavaScript代码来解析用户输入的功能(用于类似于电子表格的功能)。解析了公式之后,我可以将其转换为JavaScript并eval()
在其上运行以产生结果。
但是,我总是eval()
避免使用它,因为它是邪恶的(如果是对或错,我一直认为它在JavaScript中是更邪恶的,因为用户可能会更改要评估的代码),因此我避免使用它)。
那么,什么时候可以使用它?
我正在编写一些JavaScript代码来解析用户输入的功能(用于类似于电子表格的功能)。解析了公式之后,我可以将其转换为JavaScript并eval()
在其上运行以产生结果。
但是,我总是eval()
避免使用它,因为它是邪恶的(如果是对或错,我一直认为它在JavaScript中是更邪恶的,因为用户可能会更改要评估的代码),因此我避免使用它)。
那么,什么时候可以使用它?
Answers:
我想花点时间解决您提出问题的前提-eval()是“ 邪恶的 ”。编程语言人员通常使用“ 邪恶 ” 一词来表示“危险”,或更确切地说,“能够通过简单的命令造成很多伤害”。那么,什么时候可以使用危险的东西呢?当您知道危险所在以及采取适当的预防措施时。
到目前为止,让我们看一下使用eval()的危险。就像其他所有事物一样,可能存在许多小的潜在隐患,但是性能和代码注入是两个大隐患-eval()被认为是邪恶的原因。
根据您的具体情况。据我了解,您是在自己生成字符串,因此,假设您谨慎地不要生成“ rm -rf something-important”之类的字符串,就不会有代码注入的风险(但是请记住,这非常非常在一般情况下很难确保这一点)。另外,我相信,如果您正在浏览器中运行,则代码注入的风险很小。
至于性能,您必须权衡其易编码性。我认为,如果要解析公式,则最好在解析过程中计算结果,而不要运行另一个解析器(eval()内部的那个)。但是使用eval()进行编码可能会更容易,并且性能下降可能不会引起注意。在这种情况下,eval()看起来比其他任何可以节省您时间的函数都没有邪恶。
eval()
从您的服务器发送的javascript ,他们也可以首先更改页面的源代码,并控制用户信息。。。我看不出有什么区别。
eval()
不是邪恶的。或者,如果是这样的话,那么在其他语言中,反射,文件/网络I / O,线程和IPC就是“邪恶”的,这是邪恶的。
如果出于您的目的,eval()
它比手动解释要快,或者使您的代码更简单或更清晰,那么您应该使用它。如果两者都不是,那么您不应该这样做。就那么简单。
当您信任来源时。
对于JSON,几乎很难篡改源代码,因为它来自您控制的Web服务器。只要JSON本身不包含用户已上传的数据,使用eval就没有主要缺点。
在所有其他情况下,在将用户提供的数据馈送到eval()之前,我会竭尽全力确保用户提供的数据符合我的规则。
eval
也不会正确解析所有有效的JSON字符串。例如,JSON.parse(' "\u2028" ') === "\u2028"
但eval(' "\u2028" ')
由于U + 2028在JavaScript中是换行符而引起异常,但就JSON而言,它不是换行符。
让我们认识真正的人:
现在,每个主要的浏览器都有一个内置的控制台,您的潜在黑客可以在其中使用大量控制台来调用具有任何值的任何函数-为什么他们会费心使用eval语句-即使可以?
如果编译2000行JavaScript需要0.2秒,那么如果评估四行JSON,性能会下降吗?
甚至连克罗克福德(Crockford)对“评估都是邪恶的”的解释也很弱。
eval是邪恶的,eval函数是JavaScript中最被滥用的功能。避开它
就像克罗克福德本人可能会说的那样,“这种陈述往往会产生非理性的神经症。不要购买。”
了解评估并知道何时可能有用是更为重要的。例如,eval是评估您的软件生成的服务器响应的明智工具。
顺便说一句:Prototype.js直接调用eval五次(包括在evalJSON()和evalResponse()中)。jQuery在parseJSON中使用它(通过Function构造函数)。
我倾向于遵循克罗克福德的意见为eval()
,并完全避免。甚至似乎不需要它的方式。例如,setTimeout()
允许您传递函数而不是eval。
setTimeout(function() {
alert('hi');
}, 1000);
即使它是受信任的来源,我也不会使用它,因为JSON返回的代码可能会出现乱码,这充其量只能使某些事情变得很奇怪,最糟糕的是会暴露出一些不好的东西。
我看到人们主张不要使用eval,因为它是邪恶的,但是我看到同一个人动态地使用Function和setTimeout,所以他们在幕后使用eval :D
顺便说一句,如果您的沙箱不够确定(例如,如果您正在允许代码注入的站点上工作),那么评估是最后一个问题。安全性的基本规则是所有输入都是错误的,但是就JavaScript而言,甚至 JavaScript本身也可能是错误的,因为在JavaScript中您可以覆盖任何函数,而只是不能确保您使用的是真正的函数,因此,如果在您之前启动了恶意代码,则您将无法信任任何JavaScript内置函数:D
现在,这篇文章的结尾是:
如果你真的需要它(被EVAL 80%的时间不是需要),你肯定你”重新做,只是使用eval(或更好的功能;)),关闭和面向对象涵盖80/90%的在可以使用另一种逻辑替换eval的情况下,其余的是动态生成的代码(例如,如果您正在编写解释器),并且正如您已经说过的那样评估JSON(在这里您可以使用Crockford安全评估;))
Eval是用于模板代码的编译的补充。通过模板,我的意思是您编写了一个简化的模板生成器,该生成器生成有用的模板代码,从而提高了开发速度。
我编写了一个框架,开发人员不使用EVAL,但他们使用我们的框架,因此该框架必须使用EVAL生成模板。
可以使用以下方法来提高EVAL的性能;您必须返回一个函数,而不是执行脚本。
var a = eval("3 + 5");
它应该组织为
var f = eval("(function(a,b) { return a + b; })");
var a = f(3,5);
缓存f肯定会提高速度。
Chrome还可以非常轻松地调试此类功能。
关于安全性,是否使用eval几乎不会有什么不同,
如果您的服务器端安全性足够坚固,任何人都可以从任何地方进行攻击,则不必担心EVAL。如前所述,如果不存在EVAL,则攻击者将拥有许多工具来入侵您的服务器,而与浏览器的EVAL功能无关。
Eval仅适用于根据事先未使用的内容生成一些模板以进行复杂的字符串处理。例如,我会更喜欢
"FirstName + ' ' + LastName"
相对于
"LastName + ' ' + FirstName"
作为我的显示名称,它可以来自数据库,并且没有经过硬编码。
function (first, last) { return last + ' ' + first }
。
eval
主要来自其他用户。假设您有一个设置页面,它可以让您设置自己的名字在他人面前的显示方式。我们还假设您在编写时并没有很清楚地思考,因此您的选择框具有类似的选项<option value="LastName + ' ' + FirstName">Last First</option>
。我打开开发工具,将value
选项alert('PWNED!')
的更改为,选择更改的选项,然后提交表单。现在,只要其他人看到我的显示名称,该代码就会运行。
eval
是执行不属于您编写的脚本的代码。如果您不需要执行此操作的能力(并且几乎不需要执行此操作),则避免使用eval
有助于避免一整类问题。如果您的服务器端代码不够完美,那将是一件好事。
在Chrome浏览器(v28.0.1500.72)中进行调试时,我发现如果未在产生闭包的嵌套函数中使用变量,则它们不会绑定到闭包。我想这是对JavaScript引擎的优化。
但是:当eval()
在导致闭合的函数内部使用时,外部函数的所有变量都将绑定到闭合,即使它们根本没有使用。如果有人有时间测试是否可能导致内存泄漏,请在下面给我留言。
这是我的测试代码:
(function () {
var eval = function (arg) {
};
function evalTest() {
var used = "used";
var unused = "not used";
(function () {
used.toString(); // Variable "unused" is visible in debugger
eval("1");
})();
}
evalTest();
})();
(function () {
var eval = function (arg) {
};
function evalTest() {
var used = "used";
var unused = "not used";
(function () {
used.toString(); // Variable "unused" is NOT visible in debugger
var noval = eval;
noval("1");
})();
}
evalTest();
})();
(function () {
var noval = function (arg) {
};
function evalTest() {
var used = "used";
var unused = "not used";
(function () {
used.toString(); // Variable "unused" is NOT visible in debugger
noval("1");
})();
}
evalTest();
})();
我想在这里指出的是,eval()不一定必须引用本机eval()
函数。这一切都取决于函数的名称。因此,当eval()
使用别名(例如var noval = eval;
在内部函数中然后在内部函数中noval(expression);
)调用本机expression
时,如果对变量引用应该是闭包的一部分而实际上不是,则对的评估可能会失败。
Microsoft在IE博客IE + JavaScript性能建议第2部分:JavaScript代码效率低下解释了为什么eval()在其浏览器中运行缓慢的原因。
如果您创建或清除了您的代码eval
,那就永远不会邪恶。
eval
如果使用客户端提交的输入而不是开发者创建的或未经开发者清理的输入在服务器上运行,这是邪恶的。
eval
即使在客户端上运行,即使使用客户端精心制作的未经消毒的输入,也不是邪恶的。
显然,您应该始终清除输入内容,以便对代码使用的内容进行一些控制。
客户端可以运行他们想要的任何任意代码,即使开发人员没有对它进行编码;这不仅对于逃避的事情是正确的,而且对eval
自身的呼吁也是如此。
您应该使用eval()的唯一实例是需要动态运行动态JS的情况。我说的是您从服务器异步下载的JS ...
...十分之九,您可以轻松地通过重构避免这样做。
就客户端脚本而言,我认为安全问题是有争议的。加载到浏览器中的所有内容都应受到操纵,应这样对待。当执行JavaScript代码和/或操作DOM中的对象(例如浏览器中的URL栏)的方法更加简便时,使用eval()语句的风险为零。
javascript:alert("hello");
如果有人想操纵他们的DOM,我就说走开。防止任何类型攻击的安全性始终应由服务器应用程序负责。
从务实的角度来看,在可以以其他方式完成的情况下使用eval()没有任何好处。但是,在某些情况下应该使用eval。在这种情况下,绝对可以完成此操作而不会冒任何炸毁页面的风险。
<html>
<body>
<textarea id="output"></textarea><br/>
<input type="text" id="input" />
<button id="button" onclick="execute()">eval</button>
<script type="text/javascript">
var execute = function(){
var inputEl = document.getElementById('input');
var toEval = inputEl.value;
var outputEl = document.getElementById('output');
var output = "";
try {
output = eval(toEval);
}
catch(err){
for(var key in err){
output += key + ": " + err[key] + "\r\n";
}
}
outputEl.value = output;
}
</script>
<body>
</html>
<head></head>
即使为空也不需要吗?
在服务器端,eval在处理外部脚本(例如sql或influxdb或mongo)时很有用。无需重新部署服务即可在运行时进行自定义验证的地方。
例如,具有以下元数据的成就服务
{
"568ff113-abcd-f123-84c5-871fe2007cf0": {
"msg_enum": "quest/registration",
"timely": "all_times",
"scope": [
"quest/daily-active"
],
"query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
"validator": "valid > 0",
"reward_external": "ewallet",
"reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
},
"efdfb506-1234-abcd-9d4a-7d624c564332": {
"msg_enum": "quest/daily-active",
"timely": "daily",
"scope": [
"quest/daily-active"
],
"query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
"validator": "valid > 0",
"reward_external": "ewallet",
"reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
}
}
然后允许
通过json中的文字字符串直接注入对象/值,对于模板化文本很有用
可以用作比较器,说我们制定规则如何验证CMS中的任务或事件
缺点:
如果未经充分测试,则可能是代码错误并破坏了服务中的内容。
如果黑客可以在您的系统上编写脚本,那么您将大为困惑。
验证脚本的一种方法是将脚本的哈希值放在安全的地方,因此您可以在运行之前检查它们。
我认为评估的任何理由都是罕见的。比起实际使用它,您更有可能认为它是合理的有道理的。
安全问题是最众所周知的。但也请注意,JavaScript使用JIT编译,因此与eval一起使用时效果很差。Eval有点像编译器的黑盒,并且JavaScript需要能够(在某种程度上)提前预测代码,以便安全正确地应用性能优化和作用域。在某些情况下,性能影响甚至会影响eval之外的其他代码。
如果您想了解更多信息:https : //github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval
如果您完全控制了传递给eval
函数的代码,则可以使用它。
eval
,那么最大的问题就是,什么时候将其作为字符串而不是真正的JS有意义?
<script async="true" src="...">
。另请参阅:w3bits.com/async-javascript
只要您可以确保代码源来自您或实际用户,就没有理由不使用eval()。即使他可以操纵发送到eval()函数中的内容,但这也不是安全问题,因为他可以操纵网站的源代码,因此可以更改JavaScript代码本身。
那么...什么时候不使用eval()?仅当有可能第三方更改它时,才应使用Eval()。就像拦截客户端和服务器之间的连接一样(但是如果出现问题,请使用HTTPS)。您不应该eval()来解析论坛中其他人编写的代码。
eval
由一个用户的内容组成的字符串,就可以允许该用户在另一用户的浏览器中执行代码。
eval
的另一个浏览器。它一直在发生。
eval
。怪服务器有什么用?如果有人受到指责,那应该是攻击者。不管是什么罪魁祸首,尽管服务器中存在错误,但仍不易受到XSS攻击的客户端要比易受攻击的客户端(其他条件都相同)更好。
JavaScript的eval()什么时候不邪恶?
我一直在劝阻不要使用eval。几乎总是可以使用更清洁和可维护的解决方案。即使是JSON解析,也不需要 Eval 。评估增加了维护难度。并非没有道理,像道格拉斯·克罗克福德(Douglas Crockford)这样的大师对此并不满意。
但是我找到了一个应该在哪里的例子使用的:
例如,我有一个google.maps.ImageMapType
为我构造通用对象的函数,但是我需要告诉它配方,它应该如何从zoom
和coord
参数构造图块URL :
my_func({
name: "OSM",
tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
...
});
function my_func(opts)
{
return new google.maps.ImageMapType({
getTileUrl: function (coord, zoom) {
var b = zoom;
var a = coord;
return eval(opts.tileURLexpr);
},
....
});
}
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
我的使用示例eval
:import。
通常如何完成。
var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring
但是有了的帮助eval
和一些辅助功能,它的外观会好得多:
var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));
importable
可能看起来像(此版本不支持导入具体成员)。
function importable(path) {
var name;
var pkg = eval(path);
var result = '\n';
for (name in pkg) {
result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
}
for (name in pkg) {
result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
}
return result;
}
.replace(/name/g, name).replace('path', path)
。如果name
包含字符串,"path"
那么您可能会感到惊讶。
components
是一种可能的代码味道;重构代码可能会完全消除“问题”。您当前的解决方案只是语法糖。如果您坚持要这样做,那么我建议您编写自己的预处理器,以便在部署之前执行。那应该eval
远离生产代码。
评估不是邪恶的,只是被滥用了。
如果您创建了包含在其中的代码,或者可以信任它,那没关系。人们一直在谈论用户输入与eval无关紧要。好吧〜
如果有用户输入发送到服务器,然后又返回到客户端,则该代码将在eval中使用,而不会被清除。恭喜,您已经打开了pandora的框,可以将用户数据发送给任何人。
根据评估的位置,许多网站都使用SPA,而eval可以使用户更轻松地访问应用程序内部,否则本来就不容易。现在,他们可以制作一个伪造的浏览器扩展程序,可以将其粘贴到该评估中并再次窃取数据。
只是要弄清楚您使用评估的意义是什么。当您可以简单地创建方法来执行此类操作,使用对象等时,生成代码并不是很理想。
现在是一个使用eval的好例子。您的服务器正在读取您创建的swagger文件。许多URL参数都是以格式创建的{myParam}
。因此,您希望读取URL,然后将它们转换为模板字符串,而不必进行复杂的替换,因为您有许多端点。所以你可以做这样的事情。请注意,这是一个非常简单的示例。
const params = { id: 5 };
const route = '/api/user/{id}';
route.replace(/{/g, '${params.');
// use eval(route); to do something
代码生成。我最近写了一个名为Hyperbars的库,该库弥合了Virtual-dom和handlebars之间的差距。它通过解析车把模板并将其转换为hyperscript来实现。超级脚本首先作为字符串生成,然后在将其返回之前eval()
将其转换为可执行代码。我已经发现eval()
在这种特殊情况下,了邪恶的对立面。
基本上来自
<div>
{{#each names}}
<span>{{this}}</span>
{{/each}}
</div>
对此
(function (state) {
var Runtime = Hyperbars.Runtime;
var context = state;
return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
return [h('span', {}, [options['@index'], context])]
})])
}.bind({}))
eval()
在这种情况下,性能也不是问题,因为您只需要解释一次生成的字符串,然后多次重复使用可执行输出即可。
你可以看到,如果你好奇的代码生成是如何实现的这里。
eval
暗示一些属于编译时的职责已移至运行时。
当您没有宏时,Eval对于代码生成很有用。
对于(一个愚蠢的)示例,如果您正在编写Brainfuck编译器,则可能要构造一个函数,该函数将指令序列作为字符串执行,并求值以返回函数。
eval
。
eval
当替代项(Function)更快(如MDN中所述)且更可靠(通过更好地隔离同一网页上生成的代码与其他“支持”代码来防止不可预测的错误)时,我为Google所建议的感到震惊。
当您使用解析函数(例如jQuery.parseJSON)解析JSON结构时,它期望JSON文件具有完美的结构(每个属性名称都用双引号引起来)。但是,JavaScript更灵活。因此,可以使用eval()避免它。
eval
,尤其是。从第三方来源获取JSON数据时。看到JSON.Stringify在属性上没有引号吗?解析“没有引号的JSON名称”的正确方法。
eval
。在任何严肃的多租户Web应用程序中,数十名开发人员都在同一代码库上工作,这是不可接受的。