He estado usando la característica eval
de ruby muchas veces. Pero he escuchado gente diciendo que eval
s son desagradables. Cuando me preguntan, por qué y cómo, nunca podría obtener una razón convincente para no usarlo. ¿Son realmente desagradables? Si es así, ¿de qué manera? ¿Cuáles son las posibles opciones "más seguras" para evaluar?¿Se supone que 'eval' es desagradable?
Respuesta
Si está eval
escribiendo una cadena enviada por, o modificable por el usuario, esto es equivalente a permitir la ejecución de código arbitrario. Imagine si la cadena contiene una llamada al sistema operativo a rm -rf /
o similar. Dicho esto, en situaciones en las que se sabe que las cadenas están limitadas adecuadamente, o que el intérprete de Ruby está en una zona de pruebas adecuada, o idealmente ambas, eval
puede ser extraordinariamente poderosa.
El problema es análogo a SQL injection, si está familiarizado. La solución aquí es similar a la solución al problema de inyección (consultas parametrizadas). Es decir, si se sabe que las declaraciones que desea eval
son de una forma muy específica, y no , todas las de la declaración deben ser enviadas por el usuario, solo unas pocas variables, una expresión matemática o similar, puede tome estas piezas pequeñas del usuario, desinféctelas si es necesario, luego evalúe la declaración de la plantilla segura con la entrada del usuario conectada en los lugares apropiados.
Y la desinfección ha demostrado ser extraordinariamente difícil de hacer * correctamente * para * todos * casos. ¡Buena respuesta! – krosenvold
'rm -rf --no-preserve-root /' hoy en día. –
Hace que la depuración sea difícil. Hace la optimización difícil. Pero, sobre todo, generalmente es una señal de que hay una mejor manera de hacer lo que sea que intentes hacer.
Si nos dice lo que está tratando de lograr con eval
, puede obtener respuestas más relevantes relacionadas con su situación específica.
Eval es una característica increíblemente poderosa que debe usarse con cuidado. Además de los problemas de seguridad señalados por Matt J, también encontrará que el código evaluado de tiempo de ejecución de depuración es extremadamente difícil. Un problema en un bloque de código evaluado en tiempo de ejecución será difícil de expresar para el intérprete, por lo que será difícil buscarlo.
Dicho esto, si se siente cómodo con ese problema y no está preocupado por el problema de seguridad, entonces no debe evitar el uso de una de las características que hace que Ruby sea tan atractivo como lo es.
En ciertas situaciones, un bien ubicado eval
es inteligente y reduce la cantidad de código requerido. Además de las preocupaciones de seguridad que mencionó Matt J, también debe hacerse una pregunta muy simple:
Cuando todo está dicho y hecho, ¿alguien más puede leer su código y entender lo que hizo?
Si la respuesta es no, entonces lo que ha ganado con un eval
se abandona para su mantenimiento. Este problema no solo es aplicable si trabaja en equipo, sino que también es aplicable a usted; desea poder mirar hacia atrás en los meses de su código, si no en los próximos años, y saber lo que hizo.
En Rubí hay varios trucos que podrían ser más apropiados que eval()
:
- Hay
#send
que le permite llamar a un método cuyo nombre tiene como cadena y pasar parámetros a la misma. yield
le permite pasar un bloque de código a un método que se ejecutará en el contexto del método de recepción.- A menudo, el simple
Kernel.const_get("String")
es suficiente para obtener la clase cuyo nombre tiene como cadena.
Creo que no soy capaz de explicarlos adecuadamente en detalle, así que le acabo de dar las pistas, si está interesado, va a googlear.
Si está pasando todo lo que obtiene del "exterior" al eval
, está haciendo algo mal, y es muy desagradable. Es muy difícil de escapar el código lo suficiente como para que sea seguro, por lo que lo consideraría bastante inseguro. Sin embargo, si usa eval para evitar la duplicación u otras cosas similares, como el siguiente ejemplo de código, está bien usarlo.
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
eval "def #{symbol}; @#{symbol}; end"
end
end
define_getters :foo, :bar, :baz
end
Sin embargo, al menos en Ruby 1.9.1, Ruby tiene muy poderosos métodos de programación meta, y podría hacer lo siguiente en su lugar:
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
define_method(symbol) { instance_variable_get(symbol) }
end
end
define_getters :foo, :bar, :baz
end
Para la mayoría de propósitos, que desea utilizar estos métodos, y no se necesita escapar.
Lo malo de eval
es el hecho de que (al menos en Ruby), es bastante lento, ya que el intérprete necesita analizar la cadena y luego ejecutar el código dentro del enlace actual. Los otros métodos llaman directamente a la función C y, por lo tanto, debe obtener un aumento considerable de la velocidad.
'define_method' ha existido por mucho tiempo; no es una característica 1.9.Si está utilizando 'eval', probablemente solo signifique que no conoce la herramienta correcta para el trabajo. – Chuck
Vaya, lo siento, ahora veo que no estaba claro para nada lo que quise decir. En Ruby 1.9 tiene varios métodos nuevos de meta-programación, no me refería específicamente a 'definir_metodo'. Pero estoy de acuerdo con la mayoría de las otras personas que respondieron la publicación, el OP debe indicar por qué quiere usar 'eval'. – henrikhodne
-1. ¡Su primer 'define_getters' puede ser una vulnerabilidad! Ver un ejemplo muy similar en http://stackoverflow.com/questions/3003328/how-do-i-use-class-eval/3003509#3003509 –
eval
no solo es inseguro (como se ha señalado en otra parte), sino que también es lento. Cada vez que se ejecuta, la AST del código de edición eval
debe analizarse (y, por ejemplo, JRuby, se volvió a bytecode) de nuevo, que es una operación de cadena pesada y probablemente también sea mala para la localidad de caché (bajo la suposición de que el programa en ejecución no es mucho eval
, y las partes correspondientes del intérprete son, por lo tanto, caché-fría, además de ser grandes).
¿Por qué hay eval
en absoluto en Ruby, usted pregunta? "Porque podemos" principalmente - De hecho, cuando se inventó eval
(para el lenguaje de programación LISP), era mostly for show! Más concretamente, usar eval
es Lo correcto cuando desea "agregar un intérprete a su intérprete", para tareas de metaprogramación como escribir un preprocesador, un depurador o un motor de plantillas. La idea común para tales aplicaciones es dar masajes a un código de Ruby y llamar al eval
, y seguro que es mejor que reinventar e implementar un lenguaje de juguete específico del dominio, un error también conocido como Greenspun's Tenth Rule. Las advertencias son: tenga cuidado con los costos, por ejemplo, para un motor de plantillas, haga todos sus eval
en el momento del inicio, no en el tiempo de ejecución; y no codifique eval
que no es de confianza, a menos que sepa cómo "domarlo", es decir, seleccionar y aplicar un subconjunto seguro del lenguaje de acuerdo con la teoría de capability discipline. Este último es un lote de trabajo realmente difícil (ver, por ejemplo, how that was done for Java; no estoy al tanto de ningún esfuerzo por Ruby por desgracia).
No diría que fue inventado "para mostrar" en Lisp. Primero fue una construcción en el ámbito de la informática teórica que luego se utilizó como modelo de un intérprete (ver el [artículo de McCarty] (http://www-formal.stanford.edu/jmc/history/lisp/node3). html) en el sitio al que está enlazando). –
- 1. ¿Cómo se supone que se usa RedirectToRoute?
- 2. ¿Se supone que los comentarios Pascal anidan?
- 3. WPF: ¿Se supone que soy un diseñador?
- 4. CCD ClinicalDocument.id: ¿Cuál es el valor de la raíz que se supone que es?
- 5. ¿Para qué se supone que se utiliza la anotación javax.inject.Named?
- 6. ¿Cómo se supone que scala.util.parsing.ast.Binders se debe usar?
- 7. isReachable en Java no parece funcionar del modo que se supone que es
- 8. ¿Se supone que debo usar CanCan con Devise?
- 9. MVC, no "se supone" que usará HttpContext.Current nunca más?
- 10. ¿Qué ocurre si call_user_func se supone que devuelve false?
- 11. ¿Se supone que debes registrar el archivo R.java en VCS?
- 12. Jquery - Referencia por ID - ¿Se supone que devuelve una matriz?
- 13. ¿Se supone que los constructores variados ocultan los generados implícitamente?
- 14. ¿Qué se supone que debe hacer esta sintaxis?
- 15. ANR errors - SCREEN OFF - ¿Cómo se supone que debo manejarlos?
- 16. Hibernate EntityManager, ¿se supone que debe usarse como singleton?
- 17. ¿Qué se supone que significa una "aplicación" de Django?
- 18. ¿Cómo se supone que scrollTop en JQuery funciona?
- 19. ¿Se supone que los temporizadores EJB son persistentes/confiables?
- 20. ¿Se supone que Sanitizer.GetSafeHtmlFragment elimina los elementos <br>?
- 21. ¿No se supone que HttpContext.Current es nulo en un servicio WCF?
- 22. javascript: lanzar un objeto que se ha creado con eval
- 23. La propiedad se supone atómica por defecto
- 24. ¿Por qué eval es inseguro en javascript?
- 25. ¿Cómo se supone que funciona la API de Java Service Provide?
- 26. es ensamblador DCPU-16 'dat' con una cadena que se supone que genera un byte o palabra por carácter?
- 27. ¿La mejor práctica para establecer el valor predeterminado de un parámetro que se supone que es una lista en Python?
- 28. ¿Cómo se supone que se comportan los puntos de cancelación POSIX?
- 29. ¿No se supone que django now() se encuentra en la zona horaria predeterminada?
- 30. Bash eval replacement $() ¿no siempre es equivalente?
Las opciones más seguras dependen de lo que esté utilizando eval para hacer, ¿puede ser más específico? – rampion
No hay nada específico que quiera pedir. Pero por "más seguro" me refiero a algo que le da los mismos resultados evitando o reduciendo las vulnerabilidades. – Chirantan
se envía generalmente más seguro de usar que eval? –