为什么要避免使用exec()和eval()?

时间:2009-12-19 16:58:09

标签: python

我在多个地方多次看过这个,但从来没有找到令人满意的解释,为什么会出现这种情况。

所以,希望有人会在这里提出。为什么我们(至少一般)不使用exec()eval()

编辑:我看到人们假设这个问题与网络服务器有关 - 但事实并非如此。我可以看到为什么传递给exec的未经过类型化的字符串可能不好。在非网络应用程序中是不是很糟糕?

11 个答案:

答案 0 :(得分:19)

通常有更清晰,更直接的方法来获得相同的效果。如果您构建一个复杂的字符串并将其传递给exec,则代码很难遵循,并且难以测试。

示例:我编写的代码读取字符串键和值,并在对象中设置相应的字段。它看起来像这样:

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)

对于简单的情况,该代码并不太可怕,但随着新类型的出现,它变得越来越复杂。当有错误时,他们总是在调用exec时触发,因此堆栈跟踪并没有帮助我找到它们。最后,我切换到一个稍微长一点,不那么聪明的版本,明确地设置每个字段。

代码清晰度的第一条规则是,通过仅查看附近的行,您的代码的每一行都应该易于理解。这就是为什么不鼓励使用goto和全局变量的原因。 exec和eval可以很容易地破坏这条规则。

答案 1 :(得分:13)

当你需要exec和eval时,是的,你确实需要它们。

但是,大多数这些函数的野外使用(以及其他脚本语言中的类似结构)都是完全不合适的,可以用更快,更安全和更少错误的其他更简单的结构替换。

可以,通过适当的转义和过滤,安全地使用exec和eval。但那种直接寻求执行/解决问题的程序员(因为他们不了解语言提供的其他设施)并不是那种能够正确处理这种处理的编码器;它会成为一个不理解字符串处理并盲目地连接子字符串的人,导致脆弱的不安全代码。

这是弦乐的诱惑。在周围投掷字符串片段看起来很容易,并且让天真的编码人员认为他们理解他们正在做什么。但经验表明,在某些角落(或不是那么角落)的情况下,结果几乎总是错误的,通常具有潜在的安全隐患。这就是为什么我们说eval是邪恶的。这就是为什么我们说regex-for-HTML是邪恶的。这就是我们推动SQL参数化的原因。是的,你可以通过手动字符串处理获得所有这些东西......但除非你已经理解我们为什么说这些东西,否则你将不会

答案 2 :(得分:11)

eval()和exec()可以促进延迟编程。更重要的是,它表示正在执行的代码可能未在设计时写入,因此未经过测试。换句话说,您如何测试动态生成的代码?特别是跨浏览器。

答案 3 :(得分:10)

除了安全性之外,evalexec通常被标记为不受欢迎,因为它们会引起复杂性。当您看到eval调用时,您通常不知道其后面发生了什么,因为它会对通常位于变量中的数据起作用。这使得代码更难阅读。

调用解释器的全部功能是一项重要的武器,只能用于非常棘手的情况。但是,在大多数情况下,最好避免使用更简单的工具。

那就像所有的概括一样,要警惕这一点。在某些情况下,exec和eval可能很有价值。但是你必须有充分的理由使用它们。有关一个可接受的用途,请参阅this post

答案 4 :(得分:8)

与大多数答案在这里所说的相反,exec实际上是在Python中构建超完整装饰器的配方的一部分,因为您可以精确复制有关装饰函数的所有内容,为文档和目的生成相同的签名这样。它是广泛使用的装饰器模块(http://pypi.python.org/pypi/decorator/)功能的关键。 exec / eval必不可少的其他情况是构造任何类型的“解释Python”类型的应用程序,例如Python解析的模板语言(如Mako或Jinja)。

因此,这些功能的存在并不是“不安全”应用程序或库的直接标志。以天真的javascripty方式使用它们来评估传入的JSON或其他东西,是的,这是非常不安全的。但与往常一样,它一直在你使用它的方式,这些是非常重要的功能。

答案 5 :(得分:6)

我过去曾经使用eval()(并且仍然不时地做)在快速和肮脏的操作过程中按摩数据。它是可用于完成工作的工具包的一部分,但永远不会用于您计划在生产中使用的任何内容,例如任何命令行工具或脚本,因为所有其他答案中提到的原因。

您无法相信您的用户 - 做正确的事情。在大多数情况下,他们会,但你必须期望他们做你从未想过的所有事情,并找到你从未想到的所有错误。这正是eval()从工具变为责任的地方。

在构建QuerySet时,一个完美的例子就是使用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() 确定用于小型任务,测试和真正临时的事情,但糟糕用于永久使用,因为几乎可以肯定更好这样做的方法!

答案 6 :(得分:5)

在可能运行用户输入的上下文中允许这些功能是一个安全问题,实际工作的清理程序很难编写。

答案 7 :(得分:3)

s = "import shutil; shutil.rmtree('/nonexisting')"
eval(s)

现在假设有人可以从Web应用程序控制s,例如。

请勿尝试在您的计算机上执行此操作

答案 8 :(得分:3)

同样的原因你不应该以root用户身份登录:用脚射击自己太容易了。

答案 9 :(得分:2)

原因#1:一个安全漏洞(即编程错误......我们不能声称可以避免这些错误)并且您只是让用户访问服务器的shell。

答案 10 :(得分:2)

在交互式解释器中尝试此操作,看看会发生什么:

>>> import sys
>>> eval('{"name" : %s}' % ("sys.exit(1)"))

当然,这是一个极端的案例,但要防止这样的事情可能会很棘手。