2009-03-12 3 views
21

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

+1

более безопасные варианты зависят от того, что вы используете eval, можете ли вы быть более конкретным? – rampion

+0

Нет ничего конкретного, о чем я хочу попросить. Но «безопаснее» я имел в виду то, что дает вам те же результаты, что и избежать или уменьшать уязвимости. – Chirantan

+0

отправляется в целом безопаснее, чем eval? –

ответ

29

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

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

+0

И санировка оказалась чрезвычайно сложной для того, чтобы на самом деле * правильно делать * все * случаи. Хороший ответ! – krosenvold

+2

'rm -rf --no-preserve-root /' в настоящее время. –

7

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

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

5

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

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

5

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

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

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

10

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

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

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

-1

Если вы проходите что-либо, что вы получаете от «снаружи» до 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 

Однако, по крайней мере в Руби 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, и поэтому вы должны получить достаточно ускорение скорости.

+0

'define_method' существует уже давно - это не функция 1.9.Если вы используете 'eval', это, вероятно, означает, что вы не знаете о правильном инструменте для работы. – Chuck

+0

К сожалению, теперь я вижу, что совсем не ясно, что я имел в виду. В Ruby 1.9 у вас есть несколько новых методов метапрограммирования, я не хотел специально ссылаться на 'define_method'. Но я согласен с большинством других людей, которые ответили на этот пост, OP должен указать, почему (а) он хочет использовать 'eval'. – henrikhodne

+0

-1. Ваши первые 'define_getters' могут быть уязвимостью! См. Очень похожий пример в http://stackoverflow.com/questions/3003328/how-do-i-use-class-eval/3003509#3003509 –

8

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

Почему нет eval вообще в Ruby, спросите вы? «Потому что мы можем» в основном. Фактически, когда было изобретено eval (для языка программирования LISP), это было mostly for show! Более того, использование eval - это правильная вещь, когда вы хотите «добавить переводчика в свой интерпретатор», для задач метапрограммирования, таких как запись препроцессора, отладчика или механизма шаблонов. Общей идеей для таких приложений является массаж некоторого кода Ruby и вызов eval на нем, и он уверен, что будет изобретать и внедрять язык игрушек, специфичный для домена, и также известен как Greenspun's Tenth Rule. Оговорки: остерегайтесь затрат, например, для механизма шаблонов, все ваши eval IN во время запуска не запускаются; и не eval ненадежный код, если вы не знаете, как его «приручить», т. е. выбрать и обеспечить безопасное подмножество языка в соответствии с теорией capability discipline. Последний является лот действительно сложной работы (см. Например how that was done for Java; к сожалению, я не знаю о таких усилиях для Ruby).

+1

Я бы не сказал, что он был изобретен «для шоу» в Лиспе. Это была первая конструкция в области теоретической информатики, которая затем использовалась в качестве плана интерпретатора (см. Статью [McCarty] (http://www-formal.stanford.edu/jmc/history/lisp/node3. html) на сайте, на который вы ссылаетесь). –

 Смежные вопросы

  • Нет связанных вопросов^_^