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

Python 为什么要避免exec()和eval()?,python,Python,我在多个地方多次看到这种情况,但从未找到令人满意的解释来解释为什么会出现这种情况 所以,希望这里会有一个。为什么我们(至少通常)不应该使用exec()和eval() 编辑:我看到人们认为这个问题与web服务器有关——事实并非如此。我可以理解为什么传递给exec的未初始化字符串可能是错误的。在非web应用程序中是否不好?允许这些功能在可能运行用户输入的上下文中运行是一个安全问题,而实际工作的清理程序很难编写 s = "import shutil; shutil.rmtree('/nonexisti

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

所以,希望这里会有一个。为什么我们(至少通常)不应该使用
exec()
eval()


编辑:我看到人们认为这个问题与web服务器有关——事实并非如此。我可以理解为什么传递给
exec
的未初始化字符串可能是错误的。在非web应用程序中是否不好?

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

s = "import shutil; shutil.rmtree('/nonexisting')"
eval(s)
例如,现在假设有人可以从web应用程序控制s


不要在你的计算机上尝试这样做同样的原因,你不应该以root身份登录:这太容易让你自食其果。

原因1:一个安全缺陷(即编程错误……我们不能声称这些是可以避免的),而你刚刚给了用户访问服务器外壳的权限。

通常更清楚,获得相同效果的更直接的方法。如果构建一个复杂的字符串并将其传递给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)
这段代码对于简单的情况来说并不可怕,但随着新类型的出现,它变得越来越复杂。当出现bug时,它们总是在调用exec时触发,因此堆栈跟踪无法帮助我找到它们。最后,我切换到一个稍微长一点、不那么聪明的版本,该版本明确地设置了每个字段


代码清晰性的第一条规则是,代码的每一行都应该很容易理解,只需查看其附近的行即可。这就是为什么不鼓励使用goto和全局变量。exec和eval很容易严重违反此规则。

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

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

当然,这是一种极端情况,但要防止这种情况发生可能很棘手。

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

调用解释器的全部功能是一个沉重的武器,应该只保留在非常棘手的情况下。然而,在大多数情况下,最好避免这种情况,并且应该使用更简单的工具

这就是说,像所有的概括一样,要警惕这一点。在某些情况下,exec和eval可能很有价值。但你一定有很好的理由使用它们。有关可接受的用法,请参阅。

eval()和exec()可以促进惰性编程。更重要的是,它表明正在执行的代码可能没有在设计时编写,因此没有经过测试。换句话说,如何测试动态生成的代码?尤其是跨浏览器。

我过去(现在仍然不时)使用
eval()
在快速和肮脏的操作中处理数据。它是工具箱的一部分,可用于完成工作,但由于其他答案中提到的所有原因,不得用于生产中计划使用的任何东西,如任何命令行工具或脚本

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

一个完美的例子是在构造
QuerySet
时使用Django。传递给查询的参数接受关键字参数,如下所示:

results = Foo.objects.filter(whatever__contains='pizza')
results = eval("Foo.objects.filter(%s__%s=%s)" % (field, matcher, value))
如果以编程方式分配参数,您可能会考虑执行以下操作:

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} ) 
通过这种方式,它不仅在性能方面更快,而且更安全、更具python风格

这个故事的寓意是什么


使用
eval()
对于小任务、测试和真正临时的事情来说是正常的,但是对于永久性的使用来说是不好的,因为几乎肯定总有更好的方法

当你需要执行和评估时,是的,你真的需要它们

但是,这些函数(以及其他脚本语言中的类似结构)的大多数广泛使用是完全不合适的,可以用其他更快、更安全、bug更少的更简单结构来代替

通过正确的转义和筛选,您可以安全地使用exec和eval。但是那种直接使用exec/eval来解决问题的程序员(因为他们不了解该语言提供的其他功能)不是那种能够正确处理问题的程序员;这将是一个不理解字符串处理的人,他只是盲目地连接子字符串,导致脆弱的不安全代码


这是线的诱惑。把字符串片段扔来扔去看起来很容易,愚弄了天真的程序员,让他们以为自己明白自己在做什么。但经验表明,在某些情况下(或并非如此),结果几乎总是错误的,往往具有潜在的安全隐患。这就是为什么我们说eval是邪恶的。这就是为什么我们说HTML的正则表达式是邪恶的。这就是我们推动SQL参数化的原因。是的,您可以通过手动字符串处理来完成所有这些操作。。。但是,除非你已经理解我们为什么说这些话,否则你很可能不会。与这里大多数答案所说的相反,exec实际上是在p