"Eval" должен быть противным?

Я использовал eval особенность рубина много раз. Но я слышал, как люди говорили, что eval противны. Когда меня спрашивали, почему и как, я никогда не мог найти убедительной причины не использовать его. Они действительно противные? Если да, то каким образом? Какие возможные «более безопасные» варианты для eval?


person Chirantan    schedule 12.03.2009    source источник
comment
более безопасные варианты зависят от того, для чего вы используете eval, не могли бы вы быть более конкретными?   -  person rampion    schedule 12.03.2009
comment
Я не хочу просить ничего конкретного. Но под безопасностью я имел в виду то, что дает те же результаты, избегая или уменьшая уязвимости.   -  person Chirantan    schedule 12.03.2009
comment
Обычно использовать send безопаснее, чем eval?   -  person David West    schedule 07.03.2014
comment
send на самом деле не является альтернативой, поскольку send только отправляет сообщение объекту (то есть вызывает метод). eval оценивает код. Любой код.   -  person Chirantan    schedule 19.03.2014


Ответы (7)


Если вы eval обрабатываете строку, отправленную пользователем или изменяемую пользователем, это равносильно разрешению выполнения произвольного кода. Представьте, что строка содержит вызов ОС к rm -rf / или подобному. Тем не менее, в ситуациях, когда вы знаете, что строки надлежащим образом ограничены, или ваш интерпретатор Ruby соответствующим образом изолирован в песочнице, или в идеале и то, и другое, eval может быть чрезвычайно мощным.

Проблема аналогична SQL-инъекции, если вы знакомы. Решение здесь аналогично решению проблемы внедрения (параметризованные запросы). То есть, если известно, что операторы, которые вы хотите eval, имеют очень конкретную форму, и не все операторы должны быть отправлены пользователем, только несколько переменных, математическое выражение, или что-то подобное, вы можете взять эти маленькие кусочки от пользователя, очистить их, если необходимо, затем оценить безопасный шаблонный оператор, вставив пользовательский ввод в соответствующие места.

person Matt J    schedule 12.03.2009
comment
А дезинфекция оказалась чрезвычайно сложной для правильного выполнения всех случаев. Хороший ответ! - person krosenvold; 12.03.2009
comment
rm -rf --no-preserve-root / в наши дни. - person Cole Johnson; 17.02.2015

В Ruby есть несколько уловок, которые могут быть более подходящими, чем eval():

  1. Существует #send, который позволяет вам вызывать метод, имя которого у вас есть в виде строки, и передавать ему параметры.
  2. yield позволяет передать блок кода методу, который будет выполняться в контексте метода-получателя.
  3. Часто простого Kernel.const_get("String") достаточно, чтобы получить класс, имя которого у вас есть в виде строки.

Думаю, я не могу подробно их объяснить, поэтому я просто дал вам подсказки, если вам интересно, вы можете погуглить.

person Community    schedule 12.03.2009

eval не только небезопасен (как уже указывалось в другом месте), но и медленен. Каждый раз, когда он выполняется, AST кода evaled необходимо заново анализировать (и, например, для JRuby, преобразованного в байт-код), что представляет собой операцию с большим количеством строк и также, вероятно, плохо влияет на локальность кеша (в предположении, что запущенный программа не очень много eval, и соответствующие части интерпретатора, таким образом, не только имеют большой размер, но и слишком холодны для кеша).

Вы спросите, почему в Ruby вообще есть eval? "Потому что мы можем" в основном - На самом деле, когда eval был изобретен (для языка программирования LISP), он был в основном для шоу! Более того, использование eval - это то, что нужно, когда вы хотите «добавить интерпретатор в свой интерпретатор» для задач метапрограммирования, таких как написание препроцессора, отладчика или движка шаблонов. Общая идея для таких приложений состоит в том, чтобы массировать некоторый код Ruby и вызывать для него eval, и это определенно лучше, чем изобретать заново и внедрять специфичный для предметной области язык игрушек, - ловушка, также известная как Десятое правило Гринспана. Предостережения: остерегайтесь затрат, например, для движка шаблонов, делайте все eval во время запуска, а не во время выполнения; и не eval ненадежный код, если вы не знаете, как его "приручить", т.е. выбрать и обеспечить безопасное подмножество языка в соответствии с теорией дисциплина возможностей. Последнее - это много действительно сложной работы (см., Например, как это было сделано для Java; к сожалению, мне не известно о каких-либо подобных усилиях для Ruby).

person DomQ    schedule 27.08.2012
comment
Я бы не сказал, что это было придумано для демонстрации на Лиспе. Это была первая конструкция в области теоретической информатики, которая затем использовалась как проект интерпретатора (см. статья Маккарти на сайте, на который вы ссылаетесь). - person js.; 21.04.2015

Это затрудняет отладку. Это затрудняет оптимизацию. Но, прежде всего, это обычно знак того, что есть лучший способ делать то, что вы пытаетесь сделать.

Если вы расскажете нам, чего вы пытаетесь достичь с помощью eval, вы можете получить более релевантные ответы, относящиеся к вашему конкретному сценарию.

person recursive    schedule 12.03.2009

Eval - невероятно мощная функция, которую следует использовать с осторожностью. Помимо проблем безопасности, указанных Мэттом Дж., Вы также обнаружите, что отладка кода, оцениваемого во время выполнения, чрезвычайно сложна. Интерпретатору будет сложно выразить проблему в блоке кода, оцениваемом во время выполнения, поэтому будет сложно ее найти.

При этом, если вас устраивает эта проблема и вас не беспокоит проблема безопасности, вам не следует избегать использования одной из функций, которая делает Ruby столь же привлекательным, как он есть.

person Community    schedule 12.03.2009

В определенных ситуациях удачно размещенный eval является умным и сокращает объем необходимого кода. В дополнение к проблемам безопасности, упомянутым Мэттом Дж., Вам также необходимо задать себе один очень простой вопрос:

Когда все будет сказано и сделано, сможет ли кто-нибудь еще прочитать ваш код и понять, что вы сделали?

Если ответ отрицательный, то то, что вы получили с eval, заброшено из-за ремонтопригодности. Эта проблема не только применима, если вы работаете в команде, но также применима к вам - вы хотите иметь возможность оглянуться на свой код через несколько месяцев, если не лет, и знать, что вы сделали.

person hyuan    schedule 12.03.2009

Если вы передаете eval что-либо, полученное "извне", вы делаете что-то не так, и это очень неприятно. очень сложно избежать кода, чтобы он был безопасным, поэтому я считаю его довольно небезопасным. Однако, если вы используете eval для предотвращения дублирования или других подобных вещей, например, в следующем примере кода, его можно использовать.

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      eval "def #{symbol}; @#{symbol}; end"
    end
  end

  define_getters :foo, :bar, :baz
end

Однако, по крайней мере, в Ruby 1.9.1, Ruby имеет действительно мощные методы метапрограммирования, и вместо этого вы можете сделать следующее:

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

В большинстве случаев вы хотите использовать эти методы, и никакого экранирования не требуется.

Другой плохой момент в eval - это то, что (по крайней мере, в Ruby) он довольно медленный, так как интерпретатору необходимо проанализировать строку, а затем выполнить код внутри текущей привязки. Другие методы вызывают функцию C напрямую, и поэтому вы должны получить значительный прирост скорости.

person sarahhodne    schedule 14.12.2009
comment
define_method существует уже давно - это не функция 1.9. Если вы используете eval, это, вероятно, просто означает, что вы не знаете, какой инструмент подходит для работы. - person Chuck; 14.12.2009
comment
Ой, извините, теперь я понимаю, что было непонятно, что я имел в виду. В Ruby 1.9 есть несколько новых методов метапрограммирования, я не имел в виду конкретно define_method. Но я согласен с большинством других людей, ответивших на этот пост, OP должен указать, почему он хочет использовать eval. - person sarahhodne; 14.12.2009
comment
-1. Ваша первая define_getters может быть уязвимостью! См. Очень похожий пример в stackoverflow.com/questions/3003328/how-do-i-use-class-eval/ - person Marc-André Lafortune; 31.01.2013