好的,既然似乎有很多混乱,我会稍微简化一下这个问题。您可以尝试回答下面的原始问题,或者您可以解决此版本而忽略该行下的所有内容。
我的目标是采取任意表达并在极其受限制的环境中对其进行评估。此环境仅包含具有以下类型值的变量:
此外,表达式必然能够使用任何文字,例如数字和字符串常量(但不是数字或字符串向量,因为那些需要c
)。我想评估此环境中的表达式,并确保表达式无法访问环境之外的任何内容,以便我可以确定评估表达式不会带来安全风险。那么,在下面的代码中,你能用一个字符串填充空白,这个字符串在评估时会做些调皮吗? “顽皮的东西”被定义为在屏幕上打印某些东西,访问变量secret
的值,执行任何shell命令(最好是产生输出的命令),或者其他任何看起来很顽皮的东西(证明你的选择)
a <- 1
b <- 2
x <- 5
y <- 1:10
z <- -1
## Give secret a random value so that you can't just compute it from
## the above variables
secret <- rnorm(5)
allowed.variables <- c(
## Numeric variables
"a", "b", "x", "y", "z",
## Arithmetic operators
"(", "+", "-", "/", "*", "^", "sqrt", "log", "log10", "log2", "exp", "log1p")
restricted.environment <- Map(get, allowed.variables)
## Example naughty expressions that my method successfully guards
## against
expr1 <- "secret"
expr2 <- "cat('Printing something with cat\n')"
expr3 <- "system('echo Printing something via shell command')"
arbitrary.expression <- "?????????" # Your naughty string constant here
eval(parse(text=arbitrary.expression), envir=restricted.environment, enclos=emptyenv())
我正在编写一些代码来将算术表达式作为用户输入并进行评估。我有一组可以使用的变量,以及算术函数的白名单(+
,-
,*
,/
,^
等)。有没有什么方法可以评估表达式,以便只有这些变量和运算符在范围内,以避免任意代码注入的可能性?我有一些我觉得有用的东西,但我不想实际使用它,除非我确定它确实是防弹的:
## Shortcut for parse-then-eval pattern
evalparse <- function(expr, ...) eval(parse(text=expr), ...)
# I control these
arithmetic.operators <- Map(get, c("(", "+", "-", "/", "*", "^", "sqrt", "log", "log10", "log2", "exp", "log1p"))
vars <- list(a=1, b=2)
safe.envir <- c(vars, arithmetic.operators)
# Assume that these expressions are user input, e.g. from a web form.
nice.expr <- "a + b"
naughty.expr <- paste("cat('ARBITRARY R CODE INJECTION\n'); system('echo ARBITRARY SHELL COMMAND INJECTION');", nice.expr)
## NOT SAFE! Lookups outside env still possible.
evalparse(nice.expr, envir=safe.envir)
evalparse(naughty.expr, envir=safe.envir)
## Is this safe?
evalparse(nice.expr, envir=safe.envir, enclos=emptyenv())
evalparse(naughty.expr, envir=safe.envir, enclos=emptyenv())
如果在R中运行上面的代码,您会看到我们第一次评估naughty.expr
时,它会成功执行其有效负载。但是,第二次使用enclose=emptyenv()
,评估只能访问变量a
,b
和指定的算术运算符,因此有效负载无法执行。
那么,这个方法(即eval(..., envir=safeenv, enclos=emptyenv())
)实际上可以在生产中使用接受实际的用户输入,还是我错过了一些偷偷摸摸的方法来在resticted环境中执行任意代码?
答案 0 :(得分:10)
我会采用稍微不同的方法来定义安全函数和评估任意代码的环境,但实际上只是一些样式更改。如果safe_f
中的所有函数都是安全的,即它们不允许您执行任意代码,则此技术可证明是安全的。我非常有信心列表中的函数是安全的,但您需要检查单个源代码以确保。
safe_f <- c(
getGroupMembers("Math"),
getGroupMembers("Arith"),
getGroupMembers("Compare"),
"<-", "{", "("
)
safe_env <- new.env(parent = emptyenv())
for (f in safe_f) {
safe_env[[f]] <- get(f, "package:base")
}
safe_eval <- function(x) {
eval(substitute(x), env = safe_env)
}
# Can't access variables outside of that environment
a <- 1
safe_eval(a)
# But you can create in that environment
safe_eval(a <- 2)
# And retrieve later
safe_eval(a)
# a in the global environment is not affected
a
# You can't access dangerous functions
safe_eval(cat("Hi!"))
# And because function isn't included in the safe list
# you can't even create functions
safe_eval({
log <- function() {
stop("Danger!")
}
log()
})
这是一个比rapporter sandbox更简单的问题,因为你不是想创建一个有用的R环境,只是一个有用的计算器环境,而且要检查的函数集要小得多。