为什么应该避免使用 exec() 和 eval()?
- 2024-11-27 10:42:00
- admin 原创
- 90
问题描述:
我已在多个地方多次见到过这种情况,但从未找到令人满意的解释来解释为什么会出现这种情况。
因此,希望这里能介绍一个。为什么我们(至少一般情况下)不应该使用exec()
和eval()
?
编辑:我看到人们假设这个问题与 Web 服务器有关 - 但事实并非如此。我明白为什么传递未经清理的字符串exec
可能会很糟糕。在非 Web 应用程序中这很糟糕吗?
解决方案 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)
对于简单情况来说,该代码还不算太糟糕,但随着新类型的出现,它变得越来越复杂。当出现错误时,它们总是在调用 exec 时触发,因此堆栈跟踪无法帮助我找到它们。最终,我改用了一个稍长、不太聪明的版本,明确设置了每个字段。
代码清晰度的第一条规则是,代码的每一行都应该易于理解,只需查看其附近的行即可。这就是为什么不鼓励使用 goto 和全局变量。exec 和 eval 很容易严重违反此规则。
解决方案 2:
当您需要 exec 和 eval 时,是的,您确实需要它们。
但是,这些函数(以及其他脚本语言中的类似构造)的大部分实际使用是完全不合适的,可以用其他更快、更安全、错误更少的更简单构造来代替。
您可以通过适当的转义和过滤安全地使用 exec 和 eval。但是,直接使用 exec/eval 来解决问题的程序员(因为他们不了解该语言提供的其他功能)并不是能够正确进行处理的程序员;他们可能不了解字符串处理,只是盲目地连接子字符串,从而导致代码脆弱且不安全。
这就是字符串的诱惑。随意使用字符串段看起来很容易,并且会愚弄天真的程序员,让他们以为自己明白自己在做什么。但经验表明,在某些极端情况下(或不那么极端的情况下),结果几乎总是错误的,而且往往存在潜在的安全隐患。这就是为什么我们说 eval 是邪恶的。这就是为什么我们说 regex-for-HTML 是邪恶的。这就是为什么我们推动 SQL 参数化。是的,您可以通过手动字符串处理正确完成所有这些事情……但除非您已经理解我们为什么这么说,否则您很可能不会理解。
解决方案 3:
除了安全性之外,eval
它们exec
通常由于其引起的复杂性而被标记为不受欢迎。当您看到一个eval
调用时,您通常不知道它背后到底发生了什么,因为它对通常位于变量中的数据起作用。这使得代码更难阅读。
调用解释器的全部功能是一种重型武器,只应在非常棘手的情况下使用。然而,在大多数情况下,最好避免这样做,而应使用更简单的工具。
话虽如此,但像所有概括一样,要小心这一点。在某些情况下,exec 和 eval 可能很有价值。但你必须有充分的理由使用它们。请参阅这篇文章,了解一种可接受的用法。
解决方案 4:
eval()
并且exec()
会助长懒惰编程。更重要的是,它表明正在执行的代码可能不是在设计时编写的,因此未经测试。换句话说,你如何测试动态生成的代码?尤其是跨浏览器。
解决方案 5:
与此处大多数答案所说的相反,exec 实际上是在 Python 中构建超完整装饰器的方法的一部分,因为您可以完全复制有关装饰函数的所有内容,从而为文档等目的生成相同的签名。它是广泛使用的装饰器模块 ( http://pypi.python.org/pypi/decorator/ ) 功能的关键。exec/eval 必不可少的其他情况是构建任何类型的“解释型 Python”应用程序时,例如 Python 解析的模板语言(如 Mako 或 Jinja)。
因此,这些函数的存在并不代表应用程序或库存在“不安全”的问题。以简单的 javascripty 方式使用它们来评估传入的 JSON 或其他内容,是的,这非常不安全。但一如既往,这完全取决于您使用它的方式,而这些都是非常重要的函数。
解决方案 6:
在可能运行用户输入的环境中允许这些功能是一个安全问题,而且实际起作用的清理器很难编写。
解决方案 7:
我过去曾使用过eval()
(现在仍然时不时地使用)在快速而粗糙的操作过程中处理数据。它是可用于完成工作的工具包的一部分,但绝不能用于您计划在生产中使用的任何东西,例如任何命令行工具或脚本,因为其他答案中提到了所有原因。
你不能相信你的用户会做正确的事情。在大多数情况下,他们会这样做,但你必须期望他们做所有你从未想过的事情,并发现所有你从未预料到的错误。这正是eval()
从工具变成负担的地方。
一个完美的例子是使用 Django 构建QuerySet
。传递给查询的参数接受关键字参数,如下所示:
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()
是可以的,但对于永久使用来说却是不利的,因为几乎肯定总有更好的方法来完成它!
解决方案 8:
出于同样的原因,您不应该以 root 身份登录:这样很容易弄巧成拙。
解决方案 9:
请勿尝试在您的计算机上执行以下操作:
s = "import shutil; shutil.rmtree('/nonexisting')"
eval(s)
现在假设某人可以从 Web 应用程序控制 s。
解决方案 10:
原因#1:一个安全漏洞(即编程错误……我们不能声称这些错误可以避免)并且您刚刚授予用户访问服务器 shell 的权限。
解决方案 11:
在交互式解释器中尝试这个并看看会发生什么:
>>> import sys
>>> eval('{"name" : %s}' % ("sys.exit(1)"))
当然,这只是一个极端情况,但防止此类事情发生可能很棘手。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)