JavaScript的eval()什么时候不邪恶?


263

我正在编写一些JavaScript代码来解析用户输入的功能(用于类似于电子表格的功能)。解析了公式之后,我可以将其转换为JavaScript并eval()在其上运行以产生结果。

但是,我总是eval()避免使用它,因为它是邪恶的(如果是对或错,我一直认为它在JavaScript中是更邪恶的,因为用户可能会更改要评估的代码),因此我避免使用它)。

那么,什么时候可以使用它?


5
实际上,大多数JSON库实际上并不完全使用eval来防范安全风险。
肖恩·麦克米兰2009年

11
@Sean-JQuery和Prototype都使用eval(JQuery通过新功能使用它)
plodder 2010年

5
@plodder-您在哪里获取信息?jQuery从1.4开始就使用原生JSON.parse()(早在2010年1月)!见自己:code.jquery.com/jquery-1.4.js
肯·

3
“显然必须使用eval()来解析JSON” –相反,这是不正确的- 不应使用eval来解析JSON!使用来自json.org的 Douglas Crockfords(JSON的创建者)json2.js脚本!
TMS 2012年

11
@Tomas具有讽刺意味的是,json2.js使用eval解析JSON
tobyodavies 2012年

Answers:


262

我想花点时间解决您提出问题的前提-eval()是“ 邪恶的 ”。编程语言人员通常使用“ 邪恶 ” 一词来表示“危险”,或更确切地说,“能够通过简单的命令造成很多伤害”。那么,什么时候可以使用危险的东西呢?当您知道危险所在以及采取适当的预防措施时。

到目前为止,让我们看一下使用eval()的危险。就像其他所有事物一样,可能存在许多小的潜在隐患,但是性能和代码注入是两个大隐患-eval()被认为是邪恶的原因。

  • 性能-eval()运行解释器/编译器。如果您的代码已编译,那么这将是一个很大的成功,因为您需要在运行时中途调用可能很重的编译器。但是,JavaScript仍主要是一种解释性语言,这意味着在一般情况下调用eval()不会对性能造成很大的影响(但请参阅下面的我的特别评论)。
  • 代码注入-eval()可能以提升的特权运行一串代码。例如,以管理员/ root用户身份运行的程序永远不会希望eval()用户输入,因为该输入可能是“ rm -rf / etc / important-file”或更糟的。同样,浏览器中的JavaScript也不存在此问题,因为该程序无论如何都以用户自己的帐户运行。服务器端JavaScript可能有此问题。

根据您的具体情况。据我了解,您是在自己生成字符串,因此,假设您谨慎地不要生成“ rm -rf something-important”之类的字符串,就不会有代码注入的风险(但是请记住,这非常非常在一般情况下很难确保这一点)。另外,我相信,如果您正在浏览器中运行,则代码注入的风险很小。

至于性能,您必须权衡其易编码性。我认为,如果要解析公式,则最好在解析过程中计算结果,而不要运行另一个解析器(eval()内部的那个)。但是使用eval()进行编码可能会更容易,并且性能下降可能不会引起注意。在这种情况下,eval()看起来比其他任何可以节省您时间的函数都没有邪恶。


78
您没有解决使用eval难以调试的代码的问题
bobobobo

48
如果您完全关心用户数据,那么代码注入对于javascript是一个非常严重的问题。注入的代码将像在您的网站中一样运行(在浏览器中),从而使其可以执行用户可以手动执行的任何形式的操作。如果您允许(第三方)代码进入您的页面,它可以代表您的客户订购商品,更改其个人喜好或他们可以通过您的网站进行的任何操作。要特别小心。让黑客拥有您的客户就像让他们拥有您的服务器一样糟糕。
肖恩·麦克米兰2009年

71
如果数据来自您的服务器以及开发人员已经生成的数据,那么使用eval()不会有任何危害。真正的危害在于使您阅读的所有内容失去真实感。您会看到很多人说eval()是邪恶的,他们除了在某个地方读取它以外不知道为什么。
文斯·潘努乔

42
@Sean McMillan:我想相信您,但是如果有人要拦截并更改eval()从您的服务器发送的javascript ,他们也可以首先更改页面的源代码,并控制用户信息。。。我看不出有什么区别。
Walt W

20
关于“代码注入-...同样,浏览器中的JavaScript也不存在此问题,”&“而且,如果您在浏览器中运行,那么代码注入的风险很小,我相信。” 您是否建议浏览器中的代码注入不是问题?多年来,XSS一直在OWASP的前十名中排名前三。
Mike Samuel

72

eval()不是邪恶的。或者,如果是这样的话,那么在其他语言中,反射,文件/网络I / O,线程和IPC就是“邪恶”的,这是邪恶的。

如果出于您的目的eval()它比手动解释要快,或者使您的代码更简单或更清晰,那么您应该使用它。如果两者都不是,那么您不应该这样做。就那么简单。


5
这样的目的之一可能是生成太长或太重复而无法用手编写的优化代码。在LISP中,需要宏的东西。
wberry

5
这是一般性建议,因此可以将其应用于几乎所有存在的代码块。它确实没有为这个问题添加任何内容。特别是,它不会帮助任何人来这里确定他们的特定用法是否有问题。
jpmc26 2016年

2
更快,更简单,更清晰...此答案未充分涵盖安全隐患。
Ruud Helderman

55

当您信任来源时。

对于JSON,几乎很难篡改源代码,因为它来自您控制的Web服务器。只要JSON本身不包含用户已上传的数据,使用eval就没有主要缺点。

在所有其他情况下,在将用户提供的数据馈送到eval()之前,我会竭尽全力确保用户提供的数据符合我的规则。


13
在eval()中使用json字符串之前,应始终针对json语法对json字符串进行测试。因此json字符串“ {foo:alert('XSS')}”不会通过,因为“ alert('XSS')”不是正确的值。
Gumbo

3
好吧,那么使用HTTPS。OTOH:中间人不是花园品种Web应用程序的典型攻击方案,而跨站点脚本则是。
Tomalak

7
eval也不会正确解析所有有效的JSON字符串。例如,JSON.parse(' "\u2028" ') === "\u2028"eval(' "\u2028" ')由于U + 2028在JavaScript中是换行符而引起异常,但就JSON而言,它不是换行符。
Mike Samuel

1
@Justin-如果协议被破坏,那么通常初始页面加载将通过相同的协议发送,那么这是一个有争议的问题,因为客户端已经尽可能受到了威胁。
antinome

1
@Tomalak漂亮地说,我现在在回答中提到了这一点!太棒了!
NiCk Newman

25

让我们认识真正的人:

  1. 现在,每个主要的浏览器都有一个内置的控制台,您的潜在黑客可以在其中使用大量控制台来调用具有任何值的任何函数-为什么他们会费心使用eval语句-即使可以?

  2. 如果编译2000行JavaScript需要0.2秒,那么如果评估四行JSON,性能会下降吗?

甚至连克罗克福德(Crockford)对“评估都是邪恶的”的解释也很弱。

eval是邪恶的,eval函数是JavaScript中最被滥用的功能。避开它

就像克罗克福德本人可能会说的那样,“这种陈述往往会产生非理性的神经症。不要购买。”

了解评估并知道何时可能有用是更为重要的。例如,eval是评估您的软件生成的服务器响应的明智工具。

顺便说一句:Prototype.js直接调用eval五次(包括在evalJSON()和evalResponse()中)。jQuery在parseJSON中使用它(通过Function构造函数)。


10
JQuery使用浏览器的内置JSON.parse函数(如果可用)(更快,更安全),仅将eval用作备用机制。“评估是邪恶的”这一说法是一个相当好的准则。
jjmontes 2011年

30
关于“每个主流浏览器现在都有一个内置的控制台...”。当一个用户可以输入代码然后在另一个用户的浏览器中运行时,代码注入就是一个问题。浏览器控制台本身不允许一个用户在另一个用户的浏览器中运行代码,因此在确定是否值得防止代码注入时,它们无关紧要。
Mike Samuel

28
“现在每个主流浏览器都有一个内置的控制台……为什么他们会费心使用eval语句?” -您超标了。我建议您编辑答案。一个用户能否注入可以在另一个浏览器中运行的代码的能力是一个主要问题。这就是您需要变得真实的地方。
akkishore

5
@akkishore,如果您能提出一个支持您过度陈述的真实示例,我将不胜感激。
Akash Kava 2013年

7
@AkashKava您没有意识到的是,如果我在注释框中提交javascript,并且该javascript会将其发送到数据库。当另一个用户查看该评论(我在其中添加了javascript)时,eval将在呈现该javascript时使用该javascript,并使用解释器对其进行评估,从而使我的嵌入式javascript在其他用户的浏览器上执行。通过这样做,我可以浏览各种信息。他们的用户名,他们在数据库中的用户ID,他们的电子邮件地址等。这并不是一个很难的答案,如果您使用的是Google XSS,那么您会在大约10秒钟内看到问题所在。
凯尔·里希特

18

我倾向于遵循克罗克福德的意见eval(),并完全避免。甚至似乎不需要它的方式。例如,setTimeout()允许您传递函数而不是eval。

setTimeout(function() {
  alert('hi');
}, 1000);

即使它是受信任的来源,我也不会使用它,因为JSON返回的代码可能会出现乱码,这充其量只能使某些事情变得很奇怪,最糟糕的是会暴露出一些不好的东西。


2
我认为服务器端JSON格式化程序中的错误肯定是一个问题。服务器的响应是否取决于任何类型的用户提交的文本?然后,您必须注意XSS。
swilliams

3
如果您的Web服务器未通过HTTPS进行身份验证,则您可能会遭受中间人攻击,其中另一台主机会拦截请求并发送自己的数据。
本·康比

11
如果有人可以执行中间人攻击,那么他可以轻松地向您的脚本中注入任何内容。
el.pescado

10
您根本不应该依赖您的javascript代码...您不应该依赖于在客户端运行的任何内容...如果有人进行中间人攻击,为什么他会弄乱您的json对象?他可以为您提供不同的网页以及不同的js文件...
Calmarius 2010年

5
我个人不喜欢“总是有其他方法可以做到这一点”的论点。例如,您也可以说总是有避免面向对象编程的方法。这并不意味着它不是一个很好的选择。如果您了解评估及其危险,则可以在正确的情况下使用该工具。
达林

4

我看到人们主张不要使用eval,因为它是邪恶的,但是我看到同一个人动态地使用Function和setTimeout,所以他们在幕后使用eval :D

顺便说一句,如果您的沙箱不够确定(例如,如果您正在允许代码注入的站点上工作),那么评估是最后一个问题。安全性的基本规则是所有输入都是错误的,但是就JavaScript而言,甚至 JavaScript本身也可能是错误的,因为在JavaScript中您可以覆盖任何函数,而只是不能确保您使用的是真正的函数,因此,如果在您之前启动了恶意代码,则您将无法信任任何JavaScript内置函数:D

现在,这篇文章的结尾是:

如果你真的需要它(被EVAL 80%的时间不是需要),你肯定你”重新做,只是使用eval(或更好的功能;)),关闭和面向对象涵盖80/90%的在可以使用另一种逻辑替换eval的情况下,其余的是动态生成的代码(例如,如果您正在编写解释器),并且正如您已经说过的那样评估JSON(在这里您可以使用Crockford安全评估;))


由克罗克福德自己指出,目前的Web浏览器有一个内置的功能JSON.parse
Ruud Helderman

4

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几乎不会有什么不同,

  1. 首先,浏览器在沙箱中调用整个脚本。
  2. 任何在EVAL中有害的代码在浏览器本身中都是有害的。攻击者或任何人都可以轻松地在DOM中注入脚本节点,并且如果他/她可以评估任何内容,则可以执行任何操作。不使用EVAL不会有任何区别。
  3. 主要是较差的服务器端安全性是有害的。Cookie验证不正确或服务器上的ACL实施不正确会导致大多数攻击。
  4. Java的本机代码中存在一个最近的Java漏洞等。JavaScript过去被设计为在沙盒中运行,而小程序被设计为在具有证书等的沙盒外部运行,从而导致漏洞和许多其他情况。
  5. 编写模仿浏览器的代码并不困难。您要做的就是使用您喜欢的用户代理字符串向服务器发出HTTP请求。无论如何,所有测试工具都会模拟浏览器。如果攻击者想伤害您,则EVAL是他们的最后选择。他们还有许多其他方法来处理您的服务器端安全性。
  6. 浏览器DOM无权访问文件,而无权访问用户名。实际上,eval上的任何东西都无法访问。

如果您的服务器端安全性足够坚固,任何人都可以从任何地方进行攻击,则不必担心EVAL。如前所述,如果不存在EVAL,则攻击者将拥有许多工具来入侵您的服务器,而与浏览器的EVAL功能无关。

Eval仅适用于根据事先未使用的内容生成一些模板以进行复杂的字符串处理。例如,我会更喜欢

"FirstName + ' ' + LastName"

相对于

"LastName + ' ' + FirstName"

作为我的显示名称,它可以来自数据库,并且没有经过硬编码。


您可以使用function代替eval- function (first, last) { return last + ' ' + first }
Konrad Borowski

列的名称来自数据库。
Akash Kava 2013年

3
威胁eval主要来自其他用户。假设您有一个设置页面,它可以让您设置自己的名字在他人面前的显示方式。我们还假设您在编写时并没有很清楚地思考,因此您的选择框具有类似的选项<option value="LastName + ' ' + FirstName">Last First</option>。我打开开发工具,将value选项alert('PWNED!')的更改为,选择更改的选项,然后提交表单。现在,只要其他人看到我的显示名称,该代码就会运行。
cHao 2014年

@cHao,您正在谈论的是服务器端安全性较差的示例,服务器不应接受任何可在任何浏览器中作为代码执行的数据。再一次,您无法理解服务器端安全性差的概念。
Akash Kava

1
如果愿意,您可以抱怨服务器端的安全性,但是重点eval是执行不属于您编写的脚本的代码。如果您不需要执行此操作的能力(并且几乎不需要执行此操作),则避免使用eval有助于避免一整类问题。如果您的服务器端代码不够完美,那将是一件好事。
cHao 2013年

4

在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时,如果对变量引用应该是闭包的一部分而实际上不是,则对的评估可能会失败。



3

底线

如果您创建或清除了您的代码eval,那就永远不会邪恶

稍详细

eval如果使用客户端提交的输入而不是开发者创建的未经开发者清理的输入在服务器上运行,这是邪恶

eval即使在客户端上运行,即使使用客户端精心制作的未经消毒的输入也不邪恶

显然,您应该始终清除输入内容,以便对代码使用的内容进行一些控制。

推理

客户端可以运行他们想要的任何任意代码,即使开发人员没有对它进行编码;这不仅对于逃避的事情是正确的,而且eval自身呼吁也是如此。


2

您应该使用eval()的唯一实例是需要动态运行动态JS的情况。我说的是您从服务器异步下载的JS ...

...十分之九,您可以轻松地通过重构避免这样做。


如今,还有其他(以及更好的)从服务器异步加载JavaScript的方法:w3bits.com/async-javascript
Ruud

1

eval很少是正确的选择。虽然在很多情况下,您可以通过串联脚本并快速运行脚本来完成所需的工作,但通常可以使用更强大和可维护的技术:关联数组表示法(obj["prop"]与相同obj.prop) ,闭包,面向对象的技术,功能性技术-请改用它们。


1

就客户端脚本而言,我认为安全问题是有争议的。加载到浏览器中的所有内容都应受到操纵,应这样对待。当执行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>

6
关于“使用eval()语句在执行javascript和/或操作DOM中的对象时更容易的方法,风险为零”。当一个用户可以输入代码然后在另一个用户的浏览器中运行时,代码注入就是一个问题。浏览器控制台本身不允许一个用户在另一个用户的浏览器中运行代码,因此在确定是否值得防止代码注入时,它们无关紧要。
迈克·塞缪尔

<head></head>即使为空也不需要吗?
彼得·莫滕森

2
这个答案完全忽略了XSS的风险。
Ruud Helderman

1

在服务器端,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中的任务或事件

缺点:

  • 如果未经充分测试,则可能是代码错误并破坏了服务中的内容。

  • 如果黑客可以在您的系统上编写脚本,那么您将大为困惑。

  • 验证脚本的一种方法是将脚本的哈希值放在安全的地方,因此您可以在运行之前检查它们。


真好 当我问这个问题时,我什至没有想到服务器端JS。
理查德·特纳

1

我认为评估的任何理由都是罕见的。比起实际使用它,您更有可能认为它是合理的有道理的。

安全问题是最众所周知的。但也请注意,JavaScript使用JIT编译,因此与eval一起使用时效果很差。Eval有点像编译器的黑盒,并且JavaScript需要能够(在某种程度上)提前预测代码,以便安全正确地应用性能优化和作用域。在某些情况下,性能影响甚至会影响eval之外的其他代码。

如果您想了解更多信息:https : //github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval


0

如果您完全控制了传递给eval函数的代码,则可以使用它。


2
如果您完全控制要传递给的内容eval,那么最大的问题就是,什么时候将其作为字符串而不是真正的JS有意义?
cHao 2014年

@cHao例如,如果您有大型游戏应用程序(5-10MB Javascript),最好先构建一个简单的快速加载AJAX预加载器(1kb),该加载器加载大型Main-Script,同时显示“加载-酒吧或类似的东西。下载后,您可以使用“ eval(源)”或更好的“新功能(源)”来运行加载的Game-Application-Script。这样,用户可以直观地看到应用程序需要时间才能下载,直到游戏开始。否则,用户必须等待整个应用程序加载而没有任何视觉反馈。
SammieFox

@SammieFox还有其他(更好的方法)可以做到这一点,尤其是<script async="true" src="...">。另请参阅:w3bits.com/async-javascript
Ruud

答案是危险的建议。太多的开发人员对控制有所误解。对于不再积极维护的软件,建议确实有意义。但是此类软件应视为已失效。
Ruud Helderman

0

如果可能,仅在测试期间。另请注意,eval()比其他专用的JSON等评估器要慢得多。


0

只要您可以确保代码源来自您或实际用户,就没有理由不使用eval()。即使他可以操纵发送到eval()函数中的内容,但这也不是安全问题,因为他可以操纵网站的源代码,因此可以更改JavaScript代码本身。

那么...什么时候不使用eval()?仅当有可能第三方更改它时,才应使用Eval()。就像拦截客户端和服务器之间的连接一样(但是如果出现问题,请使用HTTPS)。您不应该eval()来解析论坛中其他人编写的代码。


关于“只要您可以确保代码源来自您或实际用户,就没有理由不使用eval()。” 假设有一个用户。该前提未在《任择议定书》中阐明。如果有多个用户,则不考虑eval由一个用户的内容组成的字符串,就可以允许该用户在另一用户的浏览器中执行代码。
Mike Samuel

@ MikeSamuel,eval可以在其他用户的浏览器中执行代码,我还没有听说过,您尝试过吗?这在浏览历史上从未发生过,您能举个例子吗?
Akash Kava 2013年

@AkashKava,字符串可以源自一个用户代理,可以存储在数据库中,然后提供给使用它eval的另一个浏览器。它一直在发生。
Mike Samuel

@MikeSamuel数据库?哪里?谁提供错误的字符串?它不是服务器端的数据库怪吗?首先,不应将EVAL归因于编写不正确的服务器端代码。使用jsfiddle并向世界展示它可能造成危害的真实示例。
Akash Kava

2
@AkashKava,我不明白你的问题。我们不是在讨论特定的应用程序,而是要使用的原因eval。怪服务器有什么用?如果有人受到指责,那应该是攻击者。不管是什么罪魁祸首,尽管服务器中存在错误,但仍不易受到XSS攻击的客户端要比易受攻击的客户端(其他条件都相同)更好。
Mike Samuel

0

如果确实需要,评估不是邪恶的。但我偶然发现的EVAL用途的99.9%没有需要(不包括的setTimeout的东西)。

对我而言,罪恶不是性能甚至安全问题(嗯,间接地是两者兼而有之)。所有这些对eval的不必要使用都会增加维护难度。重构工具被抛弃了。搜索代码很困难。这些评估的意外影响是众多的。


5
setTimeout不需要eval。您也可以在那里使用函数引用。
马修·克鲁姆利

0

JavaScript的eval()什么时候不邪恶?

我一直在劝阻不要使用eval。几乎总是可以使用更清洁和可维护的解决方案。即使是JSON解析,也不需要 Eval 。评估增加了维护难度。并非没有道理,像道格拉斯·克罗克福德(Douglas Crockford)这样的大师对此并不满意。

但是我找到了一个应该在哪里的例子使用的:

当您需要传递表达式时。

例如,我有一个google.maps.ImageMapType为我构造通用对象的函数,但是我需要告诉它配方,它应该如何从zoomcoord参数构造图块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);
        },
        ....
    });
}

3
看起来可以对其进行重构,这样就不需要eval()了-tileURLexpr只是一个模板,因此可以明智地使用replace()来完成这项工作。尽管如此,它的确使我想起了我提交问题时想到的一个示例,该示例与允许用户指定要评估的数学公式有关,类似于电子表格功能。当然,当时我没有提到这一点,因为我不想影响答案!
理查德·特纳

8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu

0

我的使用示例evalimport

通常如何完成。

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;
}

2
+1,但您这里有一个错误:.replace(/name/g, name).replace('path', path)。如果name包含字符串,"path"那么您可能会感到惊讶。
wberry 2014年

1
为每个属性声明一个变量components是一种可能的代码味道;重构代码可能会完全消除“问题”。您当前的解决方案只是语法糖。如果您坚持要这样做,那么我建议您编写自己的预处理器,以便在部署之前执行。那应该eval远离生产代码。
Ruud Helderman

0

评估不是邪恶的,只是被滥用了。

如果您创建了包含在其中的代码,或者可以信任它,那没关系。人们一直在谈论用户输入与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

-1

代码生成。我最近写了一个名为Hyperbars的库,该库弥合了Virtual-domhandlebars之间的差距。它通过解析车把模板并将其转换为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()在这种情况下,性能也不是问题,因为您只需要解释一次生成的字符串,然后多次重复使用可执行输出即可。

你可以看到,如果你好奇的代码生成是如何实现的这里


“首先将超级脚本生成为字符串(...)”,在构建阶段进行所有代码生成,将生成的超级脚本代码写入单独的可执行(.js)文件,然后部署该文件进行测试和生产。我喜欢您使用代码生成的方式。只是eval暗示一些属于编译时的职责已移至运行时。
Ruud Helderman

-1

我相信eval对于客户端Web应用程序是一个非常强大的功能,并且安全...像JavaScript一样安全,而事实并非如此。:-)安全问题本质上是服务器端问题,因为现在,使用Firebug之类的工具,您可以攻击任何JavaScript应用程序。


1
eval需要确保对XSS攻击的使用需求是安全的,而XSS攻击并不总是很容易解决。
本杰明

-1

当您没有宏时,Eval对于代码生成很有用。

对于(一个愚蠢的)示例,如果您正在编写Brainfuck编译器,则可能要构造一个函数,该函数将指令序列作为字符串执行,并求值以返回函数。


编写编译器(保存而不是执行所生成的代码)或编写解释器(每个指令具有预编译的实现)。都不是的用例eval
Ruud Helderman

如果您生成了javascript代码并想立即执行它(假设相对于直接解释而言,性能方面的优势),那将是eval的用例。
Erik Haliewicz

好点子; 我在本文中看到了有关Blockly的示例eval当替代项(Function)更快(如MDN中所述)且更可靠(通过更好地隔离同一网页上生成的代码与其他“支持”代码来防止不可预测的错误)时,我为Google所建议的感到震惊。
Ruud Helderman

-5

当您使用解析函数(例如jQuery.parseJSON)解析JSON结构时,它期望JSON文件具有完美的结构(每个属性名称都用双引号引起来)。但是,JavaScript更灵活。因此,可以使用eval()避免它。


4
请勿盲目使用eval,尤其是。从第三方来源获取JSON数据时。看到JSON.Stringify在属性上没有引号吗?解析“没有引号的JSON名称”的正确方法。
罗伯W

2
如果它不在属性名称周围使用双引号,则它可能是对象文字的字符串表示形式,但不是JSON。JSON将属性名称string定义stringa,并使用反斜杠转义
无用的代码

请参阅尼古拉斯·扎卡斯(Nikolas Zakas)的文章-“ eval()并不邪恶,只是被误解了” nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina

@vitmalina来自Zakas的文章:“如果您接受用户输入并通过eval()运行它,则可能很危险。但是,如果您的输入不是来自用户的,是否存在真正的危险?” 这就是问题所在。一旦您的代码超出了“ hello world”的范围,很快就不可能证明您没有泄漏用户的输入eval。在任何严肃的多租户Web应用程序中,数十名开发人员都在同一代码库上工作,这是不可接受的。
Ruud Helderman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.