Pregunta

He estado usando la función eval de ruby ??muchas veces. Pero he escuchado a personas decir que eval s son desagradables. Cuando me preguntaron por qué y cómo, nunca pude encontrar una razón convincente para no usarlo. ¿Son realmente desagradables? Si es así, ¿de qué manera? ¿Qué son posibles "más seguros"? opciones para evaluar?

¿Fue útil?

Solución

Si está eval utilizando una cadena enviada o modificable por el usuario, esto equivale a permitir la ejecución de código arbitrario. Imagine si la cadena contiene una llamada del sistema operativo a rm -rf / o similar. Dicho esto, en situaciones en las que sabe que las cadenas están restringidas adecuadamente, o su intérprete de Ruby está protegido adecuadamente, o idealmente ambos, eval puede ser extraordinariamente poderoso.

El problema es análogo a inyección SQL , 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 le gustaría eval son de una forma muy específica, y no all de la declaración deben ser enviadas por el usuario, solo un pocas variables, una expresión matemática o similar, puede tomar estas pequeñas piezas del usuario, desinfectarlas si es necesario, luego evaluar la declaración de plantilla segura con la entrada del usuario enchufada en los lugares apropiados.

Otros consejos

En Ruby hay varios trucos que podrían ser más apropiados que eval () :

  1. Hay #send que le permite llamar a un método cuyo nombre tiene como cadena y pasarle parámetros.
  2. 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.
  3. 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 solo te di las pistas, si te interesa, googlearás.

eval no solo es inseguro (como se ha señalado en otra parte), también es lento. Cada vez que se ejecuta, el AST del código ed eval debe analizarse (y, por ejemplo, JRuby, convertirse en bytecode) de nuevo, lo que es una operación de cadena pesada y probablemente también sea mala para el caché localidad (bajo el supuesto de que un programa en ejecución no eval mucho, y las partes correspondientes del intérprete son, por lo tanto, frías en caché, además de ser grandes).

¿Por qué hay eval en Ruby, preguntas? "Porque podemos" sobre todo: de hecho, cuando se inventó eval (para el lenguaje de programación LISP), fue principalmente para mostrar ! Más concretamente, el uso de eval es The Right Thing 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 un poco de código Ruby y llamar a eval , y seguramente supera reinventar e implementar un lenguaje de juguete específico de dominio, un escollo también conocido como Décima regla de Greenspun . Las advertencias son: tenga cuidado con los costos, por ejemplo, para un motor de plantillas, realice todas sus eval al momento del inicio, no al tiempo de ejecución; y no eval código no confiable a menos que sepa cómo "domesticar" es decir, seleccionar y aplicar un subconjunto seguro del lenguaje de acuerdo con la teoría de disciplina de capacidad . Este último es un lote de trabajo realmente difícil (vea, por ejemplo, cómo se hizo eso para Java ; desafortunadamente no estoy al tanto de tal esfuerzo para Ruby).

Hace que la depuración sea difícil. Hace que la optimización sea difícil. Pero, sobre todo, generalmente es una señal de que hay una mejor manera de hacer lo que intente hacer.

Si nos dice lo que está tratando de lograr con eval , puede obtener algunas respuestas más relevantes relacionadas con su escenario específico.

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 la depuración del código evaluado en tiempo de ejecució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 buscarlo será difícil.

Dicho esto, si te sientes cómodo con ese problema y no te preocupa el problema de seguridad, entonces no debes evitar usar una de las características que hace que Ruby sea tan atractivo como es.

En ciertas situaciones, un eval bien ubicado es inteligente y reduce la cantidad de código requerido. Además de las preocupaciones de seguridad que ha mencionado 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 deja de lado por mantenimiento. Este problema no solo es aplicable si trabaja en un equipo, sino que también es aplicable a usted: desea poder revisar su código meses, si no años, y saber lo que hizo.

Si está pasando algo de lo que obtiene del "exterior" a eval , estás haciendo algo mal, y es muy desagradable. Es muy difícil escapar del código lo suficiente como para que sea seguro, por lo que lo consideraría bastante inseguro. Sin embargo, si está utilizando 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 métodos de metaprogramación realmente potentes, 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 los propósitos, desea utilizar estos métodos, y no se necesita escapar.

La otra cosa mala 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 a la función C directamente y, por lo tanto, debería obtener un gran aumento de velocidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top