为什么要避免exec()和eval()?
我曾多次在多个地方看到过这种情况,但从来没有find令人满意的解释,为什么应该如此。
所以,希望在这里介绍一个。 为什么我们(至less,通常)不使用exec()
和eval()
?
编辑:我看到人们认为这个问题属于networking服务器 – 它不。 我可以看到为什么传递给exec
的unsanitizedstring可能是坏的。 在非networking应用程序中是不是很糟糕?
通常有更清晰,更直接的方法来达到相同的效果。 如果您构build一个复杂的string并将其传递给exec,则代码难以跟踪,并且难以testing。
例如:我编写了以string键和值读取的代码,并在对象中设置相应的字段。 它看起来像这样:
for key, val in values: fieldName = valueToFieldName[key] fieldType = fieldNameToType[fieldName] if fieldType is int: s = 'object.%s = int(%s)' % (fieldName, fieldType) #Many clauses like this... exec(s)
这个代码对于简单的情况来说并不是太可怕,但是随着新的types出现,它变得越来越复杂。 当有错误时,它们总是在调用exec时触发,所以堆栈跟踪不能帮助我find它们。 最终,我转向了一个稍微长一些,不那么聪明的版本,明确地设置每个字段。
代码清晰度的第一条规则是,只要查看代码的每一行,就可以很容易地理解代码的每一行。 这就是为什么goto和全局variables不鼓励。 exec和eval很容易打破这个规则。
当你需要exec和eval时,是的,你确实需要它们。
但是,这些函数(以及其他脚本语言中的相似结构)的大部分使用是完全不适当的,可以用更快,更安全和更less错误的其他更简单的构造来替代。
你可以通过适当的转义和过滤,安全地使用exec和eval。 但那种直接执行exec / eval来解决问题的编码器(因为他们不了解语言可用的其他设施)并不是那种能够获得正确处理的编码器; 这将是一个不理解string处理的人,只是盲目地串联子串,导致脆弱的不安全的代码。
这是string的诱惑。 扔string段看起来很容易,愚弄天真的编码人员认为他们明白他们在做什么。 但经验表明,结果在某个angular落(或不是这样的angular落)几乎总是错误的,往往会带来潜在的安全隐患。 这就是为什么我们说eval是邪恶的。 这就是为什么我们说正则expression式为HTML是邪恶的。 这就是我们推进SQL参数化的原因。 是的,你可以通过手动string处理得到所有这些东西…但是除非你已经明白我们为什么说这些东西,否则你是不可能的 。
除了安全性之外, eval
和exec
通常被标记为不合需要的,因为它们引起的复杂性。 当你看到一个eval
调用时,你通常不知道它背后发生了什么,因为它作用于通常是variables的数据。 这使得代码难以阅读。
调用翻译的全部力量是一个重要的武器,应该只保留在非常棘手的情况下。 然而,在大多数情况下,最好避免使用简单的工具。
就像所有的概括一样,要小心这个。 在某些情况下,exec和eval可能是有价值的。 但是你必须有一个很好的理由来使用它们。 看到这个职位一个可以接受的使用。
eval()和exec()可以促进懒惰的编程。 更重要的是,它表示正在执行的代码可能没有在devise时写入,因此没有经过testing。 换句话说,你如何testingdynamic生成的代码? 特别是跨浏览器。
与大多数答案在这里所说的不同,exec实际上是在Python中构build超完整装饰器的一部分,因为您可以精确地复制装饰函数的所有内容,从而为文档等目的生成相同的签名。 这是广泛使用的装饰模块( http://pypi.python.org/pypi/decorator/ )的function的关键。 其他情况下,exec / eval是必不可less的,当构build任何types的“解释Python”types的应用程序,如Pythonparsing模板语言(如Mako或Jinja)。
因此,这些function的出现并不是“不安全”应用程序或库的直接标志。 用朴素的javascripty方式来评估传入的JSON或其他东西,是的,这是非常不安全的。 但一如既往,它的一切都在你使用它的方式,这些都是非常重要的function。
我过去曾经使用过eval()
,并且在执行快速和肮脏的操作过程中对数据进行按摩。 它是可用于完成工作的工具包的一部分,但不应该用于任何计划用于生产的任何命令行工具或脚本,因为其他答案中提到的所有原因。
你永远不能相信你的用户做正确的事情。 在大多数情况下,他们会,但是你必须指望他们做你从未想过的所有事情,并find所有你从未想到的错误。 这正是eval()
从工具变为责任的地方。
一个完美的例子就是在构buildQuerySet
时使用Django。 传递给查询的参数接受关键字参数,如下所示:
results = Foo.objects.filter(whatever__contains='pizza')
如果你以编程方式分配参数,你可能会想这样做:
results = eval("Foo.objects.filter(%s__%s=%s)" % (field, matcher, value))
但总是有一个更好的方法,不使用eval()
,它通过引用传递字典:
results = Foo.objects.filter( **{'%s__%s' % (field, matcher): value} )
通过这样做,不仅性能更快,而且更安全,更加Pythonic。
故事的道德启示?
使用eval()
对于小任务,testing和真正的临时性的事情来说是可以的,但对于永久使用来说是不利的 ,因为几乎可以肯定总是有一个更好的方法来做到这一点!
在可能运行用户input的环境中允许这些function是一个安全问题,实际工作的消毒剂很难写。
同样的道理,你不应该以root的身份login:在脚下自拍太容易了。
s = "import shutil; shutil.rmtree('/nonexisting')" eval(s)
现在假设有人可以从Web应用程序中控制s,例如。
不要试图在电脑上这样做
理由#1:一个安全缺陷(即编程错误…我们不能声称这些可以避免),你只是让用户访问服务器的shell。
在交互式解释器中试试看看会发生什么:
>>> import sys >>> eval('{"name" : %s}' % ("sys.exit(1)"))
当然,这是一个特例,但要防止这样的事情可能会非常棘手。