【问题标题】:Why should exec() and eval() be avoided?为什么要避免 exec() 和 eval()?
【发布时间】:2010-12-28 08:30:08
【问题描述】:

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

所以,希望这里能展示一个。为什么我们(至少,一般来说)不应该使用exec()eval()

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

【问题讨论】:

  • 您能否提供一些来自“多个地方”的引语,以澄清您所依据的评论。重要的是要知道您阅读的内容会导致得出应避免使用的结论。这不是一个简单的问题,您需要提供您的信息来源以澄清您的问题。
  • 一般来说,据我记得(因为我多年来没有为这些地方添加书签),警告采用“当然,我们可以使用eval() 来解决这个问题,但是那会很淘气。”
  • 我想最近的例子是在程序中使用exec() 作为控制流的一种手段。
  • “不要使用 `eval” 类型的 cmets 随处可见,我每天都会遇到。这是一个:stackoverflow.com/questions/8294618/…

标签: python python-exec


【解决方案1】:

不要尝试在您的计算机上执行以下操作:

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

现在假设某人可以通过网络应用程序控制 s,例如。

【讨论】:

  • 仅供参考,这是行不通的。您只能在 eval 语句中嵌入一个表达式。这嵌入了两个语句....事实上,这可能需要一个警告,因为我刚刚意识到我刚刚在我的计算机上尝试了这两行。 :-/
  • __import__("shutil").rmtree 呢,那是一个表达式
  • "__import__('shutil').rmtree('/')" 是“工作”的变体——它是一个单一的表达式,所以eval 会很高兴地破坏你的文件系统(如果程序以足够的权限运行)。
  • 1+ 用于使用不必要的危险命令 - 并感谢 Jeffrey 进行调试。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    通常有更清晰、更直接的方法来获得相同的效果。如果您构建一个复杂的字符串并将其传递给 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 很容易破坏这条规则。

    【讨论】:

    • 稻草人参数几乎,你甚至不应该构建一个字符串传递给exec。传递文字已经够糟糕了。如果您甚至不需要它,使用 exec 不是更可怕吗? setattr 会做得更好。但是,您对 Python 的经验并不丰富。 fieldType is int?和(int)%s??
    • 它不是稻草人。将文字传递给 exec 有什么意义?至于你的其他cmets。我输入了一些快速代码来说明一个观点,希望它能够理解我的观点,并且很容易看出这种情况如何变得更加复杂。确实,我的 Python 技能有点生疏,但这与我提出的观点无关。
    • 作为参考,s = 行的更明确的写法是setattr(object, fieldName, int(fieldType))
    【解决方案4】:

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

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

    可以通过适当的转义和过滤,安全地使用 exec 和 eval。但是那种直接使用 exec/eval 来解决问题的编码器(因为他们不理解该语言提供的其他工具)不是那种能够正确处理的编码器。这将是一个不了解字符串处理并且只是盲目连接子字符串的人,从而导致脆弱的不安全代码。

    这是弦乐的诱惑。在看起来容易地抛出字符串段,并且愚弄天真的编码人员以为他们理解自己在做什么。但经验表明,在某些角落(或不那么角落)的情况下,结果几乎总是错误的,通常会带来潜在的安全隐患。这就是为什么我们说 eval 是邪恶的。这就是为什么我们说 regex-for-HTML 是邪恶的。这就是我们推动 SQL 参数化的原因。是的,您可以通过手动字符串处理来正确处理所有这些事情......但除非您已经理解我们为什么要说这些事情,否则您很可能不会。 p>

    【讨论】:

      【解决方案5】:

      我过去曾使用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} ) 
      

      通过这种方式,它不仅在性能方面更快,而且更安全,更符合 Python 风格。

      故事的寓意?

      eval() 的使用 ok 用于小任务、测试和真正临时的事情,但 bad 用于永久使用,因为几乎可以肯定总有更好的方法去做吧!

      【讨论】:

      • +1 表示在自制脚本中使用 eval() 是完全可以的。
      • 好吧,直到所说的本土脚本离开巢穴并被广泛使用,因为它们不会这样做。
      • 如果您曾经在生产中使用过装饰器模块(以及许多其他库),那么您在生产中使用过 exec。除了最终用户的数据输入之外,exec 和 eval 对许多事情都很有用。
      • 当然,但是如果你不得不问为什么应该避免使用exec()eval(),你可能不应该单独使用它们。不积极。
      【解决方案6】:

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

      【讨论】:

      • +1:“基本期望”是您拥有所有来源。使用 exec() 和 eval() 您不会 - 琐碎地 - 拥有 all 源代码。
      【解决方案7】:

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

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

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

      【讨论】:

      • 看起来好像选择了一个没有使用exec()... - 建议?
      • 回答附议。撇开安全不谈,eval 和 exec 看起来就像是维护的噩梦。
      • @Isaac,之所以选择答案,是因为我正在寻找无需执行即可完成任务的方法。在一般情况下,它对 exec 很有用,我最终使用它,因为方法变得更加随意
      【解决方案8】:

      在交互式解释器中试试这个,看看会发生什么:

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

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

      【讨论】:

      • 你也可以在没有导入任何模块的情况下使用exit()。
      • 我不记得它来自哪里,但 exit 不是内置函数。在文档的某处,它说您不应该依赖除了交互式解释器之外的这个函数。当然,如果你想闯入一个系统,这并不重要。 :-)
      【解决方案9】:

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

      【讨论】:

        【解决方案10】:

        与您不应该以 root 身份登录的原因相同:这太容易自取其辱了。

        【讨论】:

        • 也许更重要的是,让别人射你的脚。或者头。或者干脆炸掉整栋楼。
        【解决方案11】:

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

        【讨论】:

        • 所以只有在 Web 应用程序的上下文中使用才不好?
        • 所有命令行用户都受信任吗?也许你也应该关注这一点......
        • 没有。它们可能会被那些在机器上没有 root 访问权限但有权运行脚本的人用作权限提升攻击的一部分。
        • 从技术上讲,exec() 和 eval() 运行直接由用户给出或影响的输入的任何地方都是不安全的。作为一个知识渊博的用户,我可以让代码做它不应该做的事情。无论这种情况发生在哪里,都应该避免。
        猜你喜欢
        • 2013-07-05
        • 2021-06-25
        • 1970-01-01
        • 2018-11-13
        • 2011-11-15
        • 1970-01-01
        • 2011-01-14
        相关资源
        最近更新 更多