9

編集

混乱が多いように思われるので、質問を少し単純化します。以下の元の質問に答えてみることも、代わりにこのバージョンに取り組み、行の下のすべてを無視することもできます。

私の目標は、任意の式を取得し、非常に制限された環境で評価することです。この環境には、次のタイプの値を持つ変数のみが含まれます。

  • 数値ベクトル
  • 1 つ以上の数値ベクトルを取り、数値ベクトルを返す純粋な関数 (つまり、算術演算子)

さらに、式は数値定数や文字列定数などの任意のリテラルを必ず使用できます (ただし、数値ベクトルや文字列ベクトルは が必要なため、使用できませんc)。この環境で式を評価し、式が環境外のものにアクセスできないようにして、式の評価がセキュリティ リスクにならないようにしたいと考えています。では、以下のコードで、評価時に何か悪いことをする文字列を空白に埋めることができますか? 「いたずら」とは、画面に何かを出力する、変数の値にアクセスする、secret任意のシェル コマンド (できれば出力を生成するコマンド) を実行すること、またはその他のいたずらと思われること (選択を正当化すること) として定義されます。

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 で実行すると、最初の evalnaughty.exprでペイロードが正常に実行されることがわかります。ただし、 を使用した 2 回目の評価では、変数、、および指定された算術演算子にenclose=emptyenv()しかアクセスできないため、ペイロードの実行は失敗します。ab

では、このメソッド (つまりeval(..., envir=safeenv, enclos=emptyenv())) は、実際のユーザー入力を受け入れる本番環境で実際に使用しても問題ありませんか、それとも、制限された環境で任意のコードを実行するための卑劣な方法がありませんか?

4

1 に答える 1

12

安全な関数と任意のコードを評価する環境を定義するには、少し異なるアプローチをとりますが、実際にはスタイルの変更にすぎません。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()
})

これはラポーター サンドボックスよりもはるかに単純な問題です。これは、便利な R 環境を作成しようとしているのではなく、便利な計算機環境を作成しようとしているだけであり、チェックする関数のセットがはるかに小さいためです。

于 2013-08-22T22:24:54.397 に答える