¿Cuáles son específicamente los peligros de eval (parse (…))?

Hay varias preguntas sobre cómo evitar el uso de eval(parse(...))

  • r-evalparse-is-often-suboptimal
  • evitando-el-infame-evalparse-construir

Lo que provoca las preguntas:

  • ¿Por qué debería evitarse específicamente eval(parse()) ?
  • Y lo más importante, ¿cuáles son los peligros?
    • ¿Hay algún peligro si el código no se usa en producción? (Estoy pensando que existe el peligro de obtener resultados involuntarios. Claramente, si no tienes cuidado con lo que estás analizando, tendrás problemas. Pero, ¿es eso más peligroso que ser descuidado con get() ?)

La mayoría de los argumentos en contra de eval(parse(...)) surgen no por cuestiones de seguridad, después de todo, no se hacen afirmaciones sobre que R sea una interfaz segura para exponer a Internet, sino más bien porque dicho código generalmente hace cosas que puede lograrse utilizando métodos menos oscuros, es decir, métodos que son más rápidos y más analizables por el ser humano. Se supone que el lenguaje R es de alto nivel, por lo que la preferencia de los conocedores (y no me considero en ese grupo) es ver un código que sea tanto compacto como expresivo.

Entonces el peligro es que eval(parse(..)) es un método de puerta trasera para evitar la falta de conocimiento y la esperanza de elevar esa barrera es que las personas mejorarán su uso del lenguaje R. La puerta permanece abierta, pero la esperanza es para un uso más expresivo de otras características. La pregunta de Carl Witthoft el día de hoy ilustró no saber que la función get estaba disponible, y la pregunta a la que se vinculó mostró una falta de comprensión de cómo se comportaba la función [[ y cómo $ era más limitado que [[ ). En ambos casos, se podría construir una solución de eval(parse(..)) , pero era más fragmentada y menos clara que la alternativa.

Las preocupaciones de seguridad solo surgen realmente si comienza a llamar a eval en cadenas que otro usuario le ha pasado. Esto es un gran problema si está creando una aplicación que ejecuta R en segundo plano, pero para el análisis de datos donde está escribiendo código para que lo ejecute usted mismo, entonces no debería preocuparse por el efecto de eval en la seguridad.

Algunos otros problemas con eval(parse( aunque.

En primer lugar, el código que utiliza eval-parse suele ser mucho más difícil de depurar que el código no analizado, lo cual es problemático porque el software de depuración es el doble de difícil que escribirlo en primer lugar.

Aquí hay una función con un error.

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

Tonto, me olvidé del operador de colon y creé mi vector incorrectamente. Si trato de obtener esta función, entonces R nota el problema y arroja un error, indicándome mi error.

Aquí está la versión eval-parse.

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

Esto será fuente, porque el error está dentro de una cadena válida. Es solo más tarde, cuando llegamos a ejecutar el código que se produce el error. Entonces, al usar eval-parse, hemos perdido la capacidad de comprobación de errores en tiempo de origen.

También creo que esta segunda versión de la función es mucho más difícil de leer.

El otro problema con eval-parse es que es mucho más lento que el código ejecutado directamente. Comparar

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

y

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

Usualmente hay una mejor manera de ‘computar en el lenguaje’ que trabajar con cadenas de código; El código pesado evalparse necesita una gran cantidad de protección para garantizar un resultado sensato, en mi experiencia.

La misma tarea generalmente se puede resolver trabajando directamente en el código R como un objeto de lenguaje; Hadley Wickham tiene una guía útil sobre meta-progtwigción en R aquí :

La función defmacro () en la biblioteca gtools es mi sustituto favorito (no se pretende un juego de palabras R a medias) para el constructo evalparse

 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()) }  

El beneficio de este método es que tiene la garantía de recuperar el código R sintácticamente legal. Más sobre esta función útil se puede encontrar aquí :

¡Espero que ayude!

En algunos lenguajes de progtwigción, eval() es una función que evalúa una cadena como si fuera una expresión y devuelve un resultado; en otros, ejecuta múltiples líneas de código como si hubieran sido incluidas en lugar de la línea que incluye el eval. La entrada a eval no es necesariamente una cadena; en idiomas que admiten abstracciones sintácticas (como Lisp), la entrada de eval consistirá en formas sintácticas abstractas. http://en.wikipedia.org/wiki/Eval

Hay todo tipo de exploits que uno puede aprovechar si eval se usa de forma incorrecta.

Un atacante podría suministrar un progtwig con la cadena “session.update (authenticated = True)” como datos, lo que actualizaría el diccionario de sesión para establecer que una clave autenticada sea True. Para remediar esto, todos los datos que se usarán con eval se deben escapar, o se deben ejecutar sin acceso a funciones potencialmente perjudiciales. http://en.wikipedia.org/wiki/Eval

En otras palabras, el mayor peligro de eval() es el potencial para la inyección de código en su aplicación. El uso de eval() también puede causar problemas de rendimiento en algunos idiomas según lo que se está utilizando.

Específicamente en R, es probable porque puede usar get() en lugar de eval(parse()) y sus resultados serán los mismos sin tener que recurrir a eval()