我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。
然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。
那么,什么时候可以使用它呢?
我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。
然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。
那么,什么时候可以使用它呢?
当前回答
当您使用解析函数解析JSON结构时(例如,jQuery.parseJSON),它期望JSON文件的完美结构(每个属性名都用双引号括起来)。然而,JavaScript更加灵活。因此,可以使用eval()来避免它。
其他回答
如果你可以完全控制传递给eval函数的代码,那么使用它是可以的。
在服务器端,当处理sql、influxdb或mongo等外部脚本时,eval非常有用。可以在运行时进行自定义验证,而无需重新部署服务。
例如,具有以下元数据的成就服务
{ "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中的任务或事件
缺点:
如果没有完全测试,代码中的错误可能会破坏服务中的内容。 如果黑客可以在您的系统上编写脚本,那么您就完蛋了。 验证脚本的一种方法是将脚本的散列保存在安全的地方,以便在运行之前检查它们。
底线
如果你创建或净化了你评估的代码,它永远不会是邪恶的。
略为详细
如果在服务器上使用不是由开发人员创建或未经过开发人员处理的客户端提交的输入运行Eval是邪恶的。
如果在客户端上运行Eval,即使使用客户端制作的未经处理的输入,它也不是邪恶的。
显然,您应该始终清理输入,以便对代码使用的内容有一定的控制。
推理
客户端可以运行任何他们想要的代码,即使开发人员没有编写这些代码;这不仅适用于被求值的对象,而且适用于对eval本身的调用。
我想花点时间谈谈你的问题的前提——eval()是“邪恶的”。“邪恶”这个词,在编程语言的使用者中,通常意味着“危险”,或者更准确地说,“能够用一个看起来简单的命令造成很多伤害”。那么,什么时候可以使用危险的东西呢?当你知道危险是什么,并采取适当的预防措施时。
首先,让我们看看使用eval()的危险。就像其他事情一样,可能有许多小的隐患,但是两个大的风险——eval()被认为是邪恶的原因——是性能和代码注入。
Performance - eval() runs the interpreter/compiler. If your code is compiled, then this is a big hit, because you need to call a possibly-heavy compiler in the middle of run-time. However, JavaScript is still mostly an interpreted language, which means that calling eval() is not a big performance hit in the general case (but see my specific remarks below). Code injection - eval() potentially runs a string of code under elevated privileges. For example, a program running as administrator/root would never want to eval() user input, because that input could potentially be "rm -rf /etc/important-file" or worse. Again, JavaScript in a browser doesn't have that problem, because the program is running in the user's own account anyway. Server-side JavaScript could have that problem.
说到你的具体情况。根据我的理解,您自己生成字符串,所以假设您小心地不允许生成像“rm -rf something-important”这样的字符串,就没有代码注入风险(但请记住,在一般情况下很难确保这一点)。此外,如果你在浏览器中运行,那么我相信代码注入的风险是相当小的。
至于性能,您必须将其与编码的便捷性进行权衡。我的观点是,如果要解析公式,不妨在解析期间计算结果,而不是运行另一个解析器(eval()内的解析器)。但是使用eval()编码可能更容易,而且性能上的影响可能不太明显。在这种情况下,看起来eval()并不比任何其他可能为您节省时间的函数更邪恶。
Eval很少是正确的选择。虽然在许多情况下,您可以通过将脚本连接在一起并动态运行来完成需要完成的任务,但您通常可以使用更强大和可维护的技术:关联数组符号(obj["prop"]与obj.prop相同)、闭包、面向对象技术、函数技术—请使用它们。