Предполагается, что "оценка" должна быть неприятной?

StackOverflow https://stackoverflow.com/questions/637421

  •  10-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

Другие советы

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

<Ол>
  • Существует #send , который позволяет вам вызывать метод, имя которого у вас есть в виде строки, и передавать ему параметры.
  • yield позволяет передавать блок кода методу, который будет выполняться в контексте метода получения.
  • Часто простого Kernel.const_get (" String ") достаточно, чтобы получить класс, имя которого у вас есть в виде строки.
  • Я думаю, что не могу объяснить их должным образом подробно, поэтому я просто дал вам подсказки, если вы заинтересованы, вы будете Google.

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

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

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

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

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

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

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

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

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

    Если вы передаете что-либо, полученное из " снаружи " 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 напрямую, и поэтому вы должны получить значительное ускорение.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top