r-eval(parse(..))的具体危险是什么



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

  • r-evalparse-is-frequency-suboptimal
  • 避免了臭名昭著的evalparse构造

这引发了问题:

  • 为什么要特别避免eval(parse())
  • 最重要的是,有什么危险?
    • 如果不在生产中使用该代码,是否有任何危险?(我在想,任何返回意外结果的危险。很明显,如果你不小心分析什么,你就会遇到问题。但这比草率处理get()更危险吗?)

大多数反对eval(parse(...))的论点之所以出现,并不是因为安全问题,毕竟,没有人声称R是一个可以公开到互联网的安全接口,而是因为这些代码通常使用不那么晦涩的方法来完成,即既快速又更易于人工解析的方法。R语言应该是高级的,所以专家(我不认为自己在这个群体中)的偏好是看到既紧凑又有表达能力的代码。

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

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

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

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

这是一个有错误的函数。

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

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

这是eval解析版本。

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

源,因为错误位于有效字符串内。直到后来,当我们运行代码时,才抛出错误。因此,通过使用eval解析,我们已经失去了源时间错误检查功能。

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

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

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

通常有一种比处理代码字符串更好的"语言计算"方法;根据我的经验,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代码。关于这个有用功能的更多信息可以在这里找到:

希望能有所帮助!

在某些编程语言中,eval()是一个计算一个字符串,就像它是一个表达式一样,并返回一个结果;在里面在其他情况下,它执行多行代码,就好像它们included,而不是包含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()

最新更新