关于"eval是邪恶的"和"自愿成人
我看到很多人说“eval是个坏东西/危险/不安全”,因为用它可以做一些奇怪的事情,比如:
eval("os.system('rm -rf /')")
而在其他帖子中,Python程序员被认为是“同意成年人”,所以你不需要进行类型检查,因为Python采用的是一种叫做鸭子类型的风格。
那么下面的代码呢:
def foo(duck):
duck.quack()
class EvilDuck(object):
def quack(self):
os.system('rm -rf /')
foo(EvilDuck())
你通常是怎么避免这种情况的?什么时候Python程序员被认为是同意成年人,什么时候又不是呢?
3 个回答
这篇文章的顶部
在Python中使用eval是个坏习惯吗?
这个问题的回答给出了四个理由,说明为什么eval不好:
- 几乎总有更好的方法来实现同样的功能
- 非常危险且不安全
- 让调试变得困难
- 运行速度慢
从安全的角度来看,如果字符串的任何部分是从用户输入中获取的,那么使用eval
特别糟糕。清理不可信的用户输入或试图保护eval通常是没什么用的。不过,正如其他人提到的,这并不是唯一一种有类似问题的情况。反序列化不可信的用户输入,比如从pickle或json/yml/xml中获取数据,包括反序列化任意对象,也有类似的安全问题。
正如第1、3、4点所指出的,eval通常被认为是一个不太理想的解决方案,即使不考虑安全问题,只要稍微考虑一下(虽然有时这可能是一个容易忽视的实现细节),通常可以避免这些问题(比如不使用用户输入生成的字符串)。
如果可能的话,使用其他Python特性来实现同样的功能通常被认为是更好的代码,更容易维护。
尽管如此,eval在某些情况下还是有用的,标准库中的namedtuple就是用~~eval~~ exec(类似的东西)实现的。
还有人提到将它与goto进行比较,goto在某些方面“过于强大”,但在C语言中也可以非常有用。
我曾用它开发有用的调试函数和类似Ruby的字符串插值功能。
让Python去限制用户,阻止他们执行一些命令,这听起来有点傻。因为用户完全可以直接在命令行里输入这些命令,而根本不需要通过Python。
鸭子类型是可以接受的,因为它不涉及运行那些随便从网上找来的用户写的代码。你应该能够按照你定义的对象的预期方式来调用它们。
eval()
和 exec
(在Python 3中是一个函数,在Python 2中是一个语句)被认为是危险的,尤其是当它们处理未经充分验证的用户输入时。因为如果没有验证,你就可能会受到潜在恶意用户的影响。
换句话说,像这样的代码
eval(raw_input("How may I hack you today? "))
是危险的,正因为用户可能会选择输入
os.system("switch off your antivirus protection")
或者其他类似的内容,这些都可能给你带来风险。
eval
是个危险的东西,因为用户输入的内容最终会被它处理。你不需要(其实也不应该)担心代码假装不删除所有文件,因为代码本身就有能力这么做——看吧:
def foo(duck):
duck.quack()
class EvilDuck(object):
os.system('rm -rf /')
def quack(self):
pass
而且 rm -rf /
也有很大可能性不会成功哦。 ;)
简单来说,“成年人同意”就是“相信你的代码”。而 eval
则是“相信所有代码”。根据你获取代码的来源,eval
有时候可以用,但99%的情况下其实没必要,而且也很难保证它是安全的。