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

2020/10/08 10:21 · javascript ·  · 0评论

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

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

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

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

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

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

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

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

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

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

当您信任来源时。

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

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

让我们认识真正的人:

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

  2. 如果编译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是用于模板代码的编译的补充。通过模板,我的意思是您编写了一个简化的模板生成器,该生成器生成有用的模板代码,从而提高了开发速度。

我编写了一个框架,开发人员不使用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"

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

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

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

现在这篇文章的结尾是:

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

在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 ...

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

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

就客户端脚本而言,我认为安全问题是有争议的。加载到浏览器中的所有内容都应受到操纵,应这样对待。当执行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在处理外部脚本(例如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()比其他专用的JSON等评估器要慢得多。

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

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

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

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

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);
        },
        ....
    });
}

我的使用示例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;
}

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

如果您创建了可以接受的代码或可以信任它,那就没关系了。人们一直在谈论用户输入与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-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()在这种情况下,性能也不是问题,因为您只需要解释一次生成的字符串,然后多次重复使用可执行输出即可。

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

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

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

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

When you parse a JSON structure with a parse function (for example, jQuery.parseJSON), it expects a perfect structure of the JSON file (each property name is in double quotes). However, JavaScript is more flexible. Therefore, you can use eval() to avoid it.

本文地址:http://javascript.askforanswer.com/javascriptdeevalshenmeshihoubuxiee.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!