当前位置:  开发笔记 > 编程语言 > 正文

为什么使用'eval'是一种不好的做法?

如何解决《为什么使用'eval'是一种不好的做法?》经验,为你挑选了6个好方法。

我正在使用以下课程轻松存储我的歌曲数据.

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            exec 'self.%s=None'%(att.lower()) in locals()
    def setDetail(self, key, val):
        if key in self.attsToStore:
            exec 'self.%s=val'%(key.lower()) in locals()

我觉得这比写出一个if/else块更具可扩展性.但是,eval似乎被认为是一种不良做法并且使用起来不安全.如果是这样,任何人都可以向我解释为什么并告诉我一个更好的方法来定义上面的类?



1> Nadia Alraml..:

是的,使用eval是一种不好的做法.仅举几个原因:

    几乎总有一种更好的方法

    非常危险和不安全

    使调试变得困难

在您的情况下,您可以使用setattr:

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            setattr(self, att.lower(), None)
    def setDetail(self, key, val):
        if key in self.attsToStore:
            setattr(self, key.lower(), val)

编辑:

在某些情况下,您必须使用eval或exec.但它们很少见.在你的情况下使用eval肯定是一个坏习惯.我强调不好的做法,因为eval和exec经常在错误的地方使用.

编辑2:

看起来有些人不同意eval在OP情况下"非常危险且不安全".对于这个特定情况可能也是如此,但一般情况下并非如此.问题是一般性的,我列出的原因也适用于一般情况.

编辑3: 重新排序第1点和第4点


我不确定为什么纳迪亚的断言是如此有争议.对我来说似乎很简单:eval是代码注入的向量,并且以大多数其他Python函数不具备的方式存在危险.这并不意味着你根本不应该使用它,但我认为你应该明智地使用它.
@ S.Lott,不安全是一般避免eval/exec的一个非常重要的原因.许多应用程序,如网站应该格外小心.在网站中采用OP示例,该网站希望用户输入歌曲名称.它必将迟早被利用.即使是无辜的输入,如:让我们玩得开心.将导致语法错误并暴露漏洞.
-1:"非常危险和不安全"是错误的.其他三个非常清楚.请重新排序,以便2和4是前两个.如果你被邪恶的反社会者所包围,而这些反社会人士正在寻找破坏你的申请的方法,那么这只是不安全的.
@jeffjose:实际上,**它基本上是坏/邪恶,因为它将非绝对数据视为代码(这就是为什么存在XSS,SQL注入和堆栈碎片).@ S.Lott:"如果你被邪恶的反社会人士所包围,并且正在寻找破坏你申请的方法,那么这只是不安全的." 很酷,所以说你制作一个程序`calc`,并添加数字,它执行`print(eval("{} + {}".format(n1,n2)))`并退出.现在您使用某些操作系统分发此程序.然后有人制作了一个bash脚本,它从一个库存网站获取一些数字并使用`calc`添加它们.繁荣?
@Nadia Alramli:用户输入和'eval`彼此无关.从根本上错误设计的应用程序根本就是错误设计的.`eval`不再是设计不良的根本原因,而不是除零或试图导入已知不存在的模块.`eval`并不安全.应用程序不安全.
@Selinap,eval具有实际用例,但它们很少见且特别.在OP的情况下,非常清楚eval是一个糟糕且不安全的选择.如果存在某种并不意味着我们应该盲目地使用它.
我和Owen S. Saying说'eval`"不危险或不安全"有点像说"手工制作的SQL不危险或不安全".当然,可能有一些罕见的用例需要或时间你根本不关心,因为安全性不是你的应用程序的问题,但这些都没有否定"eval"能够非常容易地使你的应用程序的事实易受外界攻击."非常容易让我的应用程序容易受到外部攻击"听起来像是认为"非常危险和不安全"的合理资格.
@Denis,你是对的OP案例没有用户数据.但问题仍然是笼统的:"在Python中使用eval是一种不好的做法吗?"
@ S.Lott:可能更好的说'eval`是理智的**当且仅当**使用`eval`的方法不会执行疯狂的代码作为副作用...说我有一个JSON解码器JavaScript ...我想传递**任何**给我,我不想浪费我的时间每次调用它时编写清理代码(如果函数刚刚返回`eval(输入)那就是这种情况) .
看,每个使用Python的人都没有编写安全性是主要问题的Web脚本或应用程序.所以,虽然我同意通常有更好的方法,比如使用`setattr()`,并且应该努力找到它们以获得更好的设计,我绝对不同意eval/exec本身就是**"非常危险和不安全".他们有充足的理由,他们提供的能力是Python等解释性语言最酷,最强大的功能之一.
@martineau:与替代品相比,它本质上是危险和不安全的.如果/当你没有其他选择时,没有什么比较它可以*可以*可以使用.
我同意S.Lott:执行语句中没有用户数据,因此没有理由认为它不安全.
S.Lott的观点确实有效.`eval`从根本上说不是坏/坏.它的设计几乎总是应该归咎于它.如果你的设计不能保证你不会恶意用户输入,那么`eval`可能不是你应该担心的.

2> S.Lott..:

使用eval很弱,不是一个明显不好的做法.

    它违反了"软件基本原理".您的来源不是可执行文件的总和.除了你的来源之外,还有eval必须清楚理解的论据.因此,它是最后的工具.

    这通常是轻率设计的标志.动态源代码很少有充分的理由,即时构建.使用委托和其他OO设计技术几乎可以做任何事情.

    它导致相对较慢的动态编译小块代码.通过使用更好的设计模式可以避免开销.

作为一个脚注,在疯狂的反社会手中,它可能不会很好.然而,当面对精神错乱的反社会用户或管理员时,最好不要首先给他们解释Python.在真正邪恶的手中,Python可以承担责任; eval根本不会增加风险.


好吧,我可以告诉你为什么我会说eval是一个安全漏洞,它与它作为输入给出的字符串的可信度有关.如果该字符串全部或部分来自外部世界,如果您不小心,可能会对您的程序进行脚本攻击.但这是外部攻击者的紊乱,而不是用户或管理员的紊乱.
@OwenS.:对于一串不受信任的Python代码来说,没有可能的转义可以让它变得可信.除了"细心"的部分,我同意你所说的大部分内容.这是一个非常清晰的区别.来自外部世界的代码是不可信的.AFAIK,没有任何逃逸或过滤可以清理它.如果您有某种使代码可以接受的转义功能,请分享.我不认为这样的事情是可能的.例如`while True:pass`很难通过某种转义来清理.
@Owen S.关键在于此.人们会告诉你,'eval`是某种"安全漏洞".好像Python本身 - 不仅仅是一堆任何人都可以修改的解释源.当面对"eval是一个安全漏洞"时,你只能假设它是反社会人士手中的安全漏洞.普通的程序员只是修改现有的Python源并直接导致他们的问题.不是间接通过`eval`魔术.
@OwenS.:"如果该字符串全部或部分来自外部世界"通常是错误的.这不是一个"小心"的事情.它是黑色和白色.如果文本来自用户,则它永远不会被信任.护理不是它的一部分,它绝对不可信.否则,文本来自开发人员,安装人员或管理员,并且可以信任.
@OwenS.:"打算作为字符串,而不是任意代码".那是无关紧要的.这只是一个字符串值,你永远不会通过`eval()`,因为它是一个字符串.来自"外部世界"的代码无法进行消毒.来自外部世界的字符串只是字符串.我不清楚你在谈论什么.也许您应该提供更完整的博客文章并在此处链接到它.

3> Josh Lee..:

在这种情况下,是的.代替

exec 'self.Foo=val'

你应该使用内置函数setattr:

setattr(self, 'Foo', val)



4> Hackaholic..:

是的:

使用Python的Hack:

>>> eval(input())
"__import__('os').listdir('.')"
...........
...........   #dir listing
...........

以下代码将列出在Windows计算机上运行的所有任务.

>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"

在Linux中:

>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"



5> Robert Rossn..:

值得注意的是,对于所讨论的具体问题,有几种替代方法可供使用eval:

如上所述,最简单的是使用setattr:

def __init__(self):
    for name in attsToStore:
        setattr(self, name, None)

一种不太明显的方法是__dict__直接更新对象的对象.如果你想要做的就是将属性初始化为None,那么这比上面的要简单得多.但考虑一下:

def __init__(self, **kwargs):
    for name in self.attsToStore:
       self.__dict__[name] = kwargs.get(name, None)

这允许您将关键字参数传递给构造函数,例如:

s = Song(name='History', artist='The Verve')

它还允许您locals()更明确地使用,例如:

s = Song(**locals())

...并且,如果您真的想要分配None名称在locals()以下位置找到的属性:

s = Song(**dict([(k, None) for k in locals().keys()]))

为对象提供属性列表的默认值的另一种方法是定义类的__getattr__方法:

def __getattr__(self, name):
    if name in self.attsToStore:
        return None
    raise NameError, name

当以正常方式找不到命名属性时,将调用此方法.这种方法比简单地在构造函数中设置属性或更新__dict__它更简单,但它具有不实际创建属性的优点,除非它存在,这可以大大减少类的内存使用.

所有这一切:一般来说,有很多原因可以避免eval- 执行你无法控制的代码的安全问题,你无法调试的代码的实际问题等等.但更重要的原因一般来说,你不需要使用它.Python向程序员公开了很多内部机制,你很少需要编写编写代码的代码.



6> Jim Fasaraki..:

其他用户指出如何更改代码以使其不依赖eval; 我将提供一个合法的用例eval,即使在CPython中也可以找到:测试.

这里有一个例子,我在发现test_unary.py其中是否测试(+|-|~)b'a'提出了一个TypeError:

def test_bad_types(self):
    for op in '+', '-', '~':
        self.assertRaises(TypeError, eval, op + "b'a'")
        self.assertRaises(TypeError, eval, op + "'a'")

这里的用法显然不错; 您定义输入并仅观察行为.eval很方便测试.

看看这个搜索在为eval,在CPython的Git仓库中进行; 使用eval进行测试的用量很大.

推荐阅读
oDavid_仔o_880
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有