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

但是,如果可以避免使用eval(),我总是避免使用它,因为它很邪恶(而且,是非是非,我一直认为JavaScript会更加邪恶,因为要评估的代码可能会被用户更改。)

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

评论

实际上,大多数JSON库实际上并不是在幕后使用eval来完全防范安全风险。

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

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

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

@Tomas具有讽刺意味的是json2.js使用eval解析JSON

#1 楼

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

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

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

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

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

评论


您没有解决使用eval进行调试的代码问题

– bobobobo
09年8月29日在18:45

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

– Sean McMillan
09年12月14日在17:19

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

–剃刀
10 Mar 4 '10 at 5:38

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

–华特W
2010年4月1日于17:25

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

–迈克·塞缪尔(Mike Samuel)
2012年8月3日在22:42



#2 楼

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

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

评论


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

–wberry
2014年3月25日15:39

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

– jpmc26
16-10-4在18:29



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

– Ruud Helderman
19年7月14日在20:28

#3 楼

当您信任来源时。

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

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

评论


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

–浓汤
09年2月11日在12:52

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

– Tomalak
09年12月15日在18:50

eval也不会正确解析所有有效的JSON字符串。例如JSON.parse('“ \ u2028”')===“ \ u2028”但eval('“ \ u2028”')引发异常,因为U + 2028是JavaScript中的换行符,但到目前为止不是换行符就JSON而言。

–迈克·塞缪尔(Mike Samuel)
2012年8月3日在22:36

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

–抗菌素
2013年11月6日18:38



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

– NiCk Newman
15年8月1日在11:06

#4 楼

让我们成为真正的人们:


现在,每个主流浏览器都有一个内置的控制台,您想要成为黑客的人可以大量使用它来调用具有任何值的任何功能-为什么他们要打扰到使用eval语句-即使它们可以?
如果编译2000行JavaScript需要0.2秒,那么如果我评估四行JSON会对我的性能造成什么影响?

甚至Crockford的解释因为“评估是邪恶的”是软弱的。


eval是邪恶的,eval函数是
JavaScript中最被滥用的功能。避免这样做


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

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

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

评论


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

– jjmontes
2011年9月12日13:21在

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

–迈克·塞缪尔(Mike Samuel)
2012年8月3日在22:29

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

– akkishore
2012-12-27 8:24



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

– Akash Kava
13年4月19日在10:31

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

–凯尔·里希特(Kyle Richter)
2014年8月5日在22:43

#5 楼

对于eval(),我倾向于遵循克罗克福德的建议,完全避免使用。甚至似乎不需要它的方式。例如,setTimeout()允许您传递函数而不是eval。通过JSON可能会出现乱码,这充其量只能使某些事情变得不可靠,最坏的情况下会暴露出一些不好的东西。

评论


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

–威廉姆斯
08-10-13在15:06

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

– Ben Combee
08年12月21日在0:18

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

–el.pescado
2010年5月5日17:11

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

–卡尔马留斯
2010年7月1日于13:24

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

–达林
13年5月13日在18:20

#6 楼

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

如果您的服务器端安全性足够坚固,任何人都可以从任何地方进行攻击,则不必担心EVAL。正如我提到的,如果不存在EVAL,则无论浏览器的EVAL功能如何,攻击者都有许多工具可以入侵您的服务器。事先不使用。例如,相对于

"FirstName + ' ' + LastName"


,我会更喜欢

"LastName + ' ' + FirstName"


作为我的显示名称,它可以来自数据库且未进行硬编码。

评论


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

– Konrad Borowski
13-10-15在19:10

列的名称来自数据库。

– Akash Kava
13-10-15在19:49

评估的威胁主要是其他用户。假设您有一个设置页面,它可以让您设置自己的名字在他人面前的显示方式。假设您在编写时并没有很清楚地思考,因此您的选择框具有诸如

#7 楼

我看到人们主张不要使用eval,因为它是邪恶的,但是我看到同一个人动态地使用Function和setTimeout,所以如果您不确定沙盒,他们会在后台使用eval:D

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

现在,本文的结尾是:

如果您真正需要它, (不需要eval的80%的时间),并且您确定自己在做什么,只需使用eval(或更好的Function;)),闭包和OOP可以覆盖80/90%的eval情况用另一种逻辑替换,其余的是动态生成的代码(例如,如果您正在编写解释器),并且正如您已经说过的那样评估JSON(在这里您可以使用Crockford安全评估;)))

评论


而且正如Crockford本人所指出的那样,当前的Web浏览器具有内置函数JSON.parse。

– Ruud Helderman
19年7月14日在15:40

#8 楼

在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引用应作为闭包的一部分但实际上不是的变量时,其求值可能会失败。

#9 楼

Microsoft在IE博客IE + JavaScript性能建议第2部分:JavaScript代码效率低下解释了为什么eval()在其浏览器中运行缓慢的原因。

#10 楼

底线

如果您创建或清理了代码eval,它永远不会有害。

稍​​微详细一点

eval在运行时是有害的如果服务器在客户端上运行,即使使用由开发人员精心制作的未经消毒的输入,eval也不是邪恶的。客户端。

显然,您应该始终清理输入内容,以便对您的代码使用的内容进行一些控制。他们想要的任何任意代码,即使开发人员未编写代码;这不仅对于逃避的事情是正确的,而且对eval本身的调用也是如此。

#11 楼

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

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

评论


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

– Ruud Helderman
19年7月14日在15:31

#12 楼

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

#13 楼

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


评论


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

–迈克·塞缪尔(Mike Samuel)
2012年8月3日在22:30

即使为空,也不需要 吗?

– Peter Mortensen
17年1月13日在22:57

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

– Ruud Helderman
19年7月14日在14:56

#14 楼

在服务器端,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。

–理查德·特纳(Richard Turner)
18年2月14日在8:48

#15 楼

我认为任何评估理由都是合理的。与实际使用时相比,您更有可能认为它是合理的。

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

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

#16 楼

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

评论


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

– cHao
2014年1月15日13:59



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

–SammieFox
17年4月11日在18:08

@SammieFox还有其他(以及更好的)方法可以做到这一点,最著名的是