如果你正在评估由用户提交或可修改的字符串,那么这等同于允许任意代码执行。想象一下,如果该字符串包含对操作系统调用rm -rf /
或类似操作的指令,后果将不堪设想。尽管如此,在您知道字符串已适当受限、Ruby解释器已适当隔离或理想情况下两者都是如此的情况下,eval
可以变得非常强大。
这个问题类似于SQL注入问题,如果您熟悉的话。解决方案与注入问题(参数化查询)类似。也就是说,如果您想要使用 eval
的语句是已知为特定形式,并且不需要全部都由用户提交,只需要几个变量、一个数学表达式或类似的内容,您可以从用户那里获取这些小片段,必要时对它们进行消毒处理,然后在适当的位置插入用户输入数据,执行安全模板语句。
eval
不仅存在安全问题(正如其他地方指出的那样),而且执行速度也很慢。每次执行eval
时,需要重新解析eval
代码的AST,并且对于例如JRuby而言,还需要将其转换为新的字节码,这是一种字符串密集型操作,也可能对缓存局部性产生负面影响(假设运行程序不会过多使用eval
,因此解释器的相关部分也会较冷,除了占用空间较大外)。
你会问,为什么Ruby中会有eval
?主要是因为“我们可以” - 实际上,在发明eval
时(用于LISP编程语言),它主要是为了炫耀而存在的!点一下链接查看详情。更重要的是,在元编程任务中,当您想要“在您的解释器中添加一个解释器”时,使用eval
是正确的选择,例如编写预处理器、调试器或模板引擎。通常的思路是,修改一些Ruby代码并对其调用eval
,这比重新发明和实现特定领域的小语言要强得多,这种坑也称为格林斯潘第十条规则。需要注意的是:要注意成本,例如对于模板引擎,应在启动时而不是运行时执行所有eval
操作;如果不知道如何“驯服”它,请勿对不受信任的代码进行eval
操作,即根据能力学科理论选择并强制执行语言的安全子集。后者是非常困难的工作(请参见例如Java是如何实现的!点一下链接查看详情;不幸的是,我不知道是否有类似Ruby的努力)。
Ruby中有几个技巧可能比eval()
更合适:
#send
允许您调用一个方法,其名称为字符串,并传递参数。yield
允许您将一段代码块传递给一个方法,在接收方法的上下文中执行。Kernel.const_get("String")
足以获取其名称为字符串的类。我认为我无法详细地解释它们,所以我只是给了你提示,如果你感兴趣,你可以去搜索一下。
Eval是一项非常强大的功能,但需要谨慎使用。除了Matt J指出的安全问题外,您还会发现调试运行时评估的代码非常困难。运行时评估代码块中的问题将很难被解释器表达 - 因此查找它将会很困难。
话虽如此,如果您对此问题感到舒适,并且不担心安全问题,则不应避免使用使Ruby如此吸引人的功能之一。
eval
可以聪明地减少所需代码的数量。除了Matt J提到的安全问题外,你还需要问自己一个非常简单的问题:eval
得到的东西就被放弃了,因为它不易于维护。这个问题不仅适用于团队工作,也适用于你自己——你希望在几个月或几年后回顾你的代码时知道你做了什么。使用eval
方法本身并没有什么问题,然而,要求使用它通常是弱架构设计和设计模式决策的标志。
我作为一名全职Ruby开发人员已经工作了12年,我从来没有在我的任何代码库中使用过eval,但是当我在现有的代码库中发现它时,总是为了绕过已经存在的复杂性。
它还带来了巨大的安全风险,因为任何动态值都会根据应用程序用户帐户在实际服务器上进行评估。
如果你将任何从“外部”获取的东西传递给eval
,那么你正在做一些错误的事情,而且非常危险。很难逃脱足够的代码以使其安全,因此我认为它相当不安全。但是,如果你使用eval来避免重复或其他类似的事情,就像以下代码示例一样,那么使用它是可以的。
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
eval "def #{symbol}; @#{symbol}; end"
end
end
define_getters :foo, :bar, :baz
end
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
define_method(symbol) { instance_variable_get(symbol) }
end
end
define_getters :foo, :bar, :baz
end
对于大多数情况,您希望使用这些方法,不需要进行任何转义。
eval
的另一个问题是(至少在 Ruby 中),它非常慢,因为解释器需要解析字符串,然后执行当前绑定内部的代码。其他方法直接调用 C 函数,因此您应该获得相当大的速度提升。
define_method
已经存在很长时间了 - 这不是 1.9 版本的特性。如果你正在使用 eval
,那可能只是因为你不知道适合这项工作的正确工具。 - Chuckdefine_method
。但我同意大多数其他回答帖子的人的观点,OP应该说明为什么要使用eval
。 - sarahhodnedefine_getters
可能存在漏洞!请参考 https://dev59.com/JnA75IYBdhLWcg3w-OVA#3003509 中非常相似的例子。 - Marc-André Lafortune