70

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

这引发了以下问题:

  • 为什么具体应该eval(parse())避免?
  • 最重要的是,有什么危险?
    • 如果代码没有在生产中使用,会有什么危险吗?(我在想,任何返回意外结果的危险。显然,如果你不小心你正在解析的内容,你会遇到问题。但这比马虎更危险get()吗?)
4

4 回答 4

42

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

所以危险在于这eval(parse(..))是一种绕过知识匮乏的后门方法,而提高这一障碍的希望是人们将改善他们对 R 语言的使用。大门仍然敞开,但希望能够更有表现力地使用其他功能。Carl Witthoft 今天早些时候提出的问题说明了不知道该get函数是否可用,他所链接的问题[[暴露了对该函数的行为方式缺乏了解(以及如何$比 更受限制[[)。在这两种情况下,eval(parse(..))都可以构建一个解决方案,但它比替代方案更笨拙且不够清晰。

于 2012-11-30T18:26:21.753 回答
36

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

不过还有其他一些问题eval(parse(

首先,使用 eval-parse 的代码通常比未解析的代码更难调试,这是有问题的,因为调试软件的难度是最初编写软件的两倍。

这是一个有错误的函数。

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
于 2012-12-01T15:05:34.527 回答
18

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

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

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 代码。更多关于这个有用的功能可以在这里找到:

希望有帮助!

于 2012-12-02T10:44:41.930 回答
8

在某些编程语言中,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()

于 2012-11-30T17:43:33.673 回答