Что конкретно представляют опасности eval (parse (...))?

Существует несколько вопросов о том, как избежать использования eval(parse(...))

Что вызывает вопросы:

  • Почему конкретно следует избегать eval(parse())?
  • И самое главное, Каковы опасности?
    • Есть ли опасность, если код не используется в производстве? (Я думаю, любая опасность вернуть непредвиденные результаты. Ясно, что если вы не будете осторожны в том, что вы разбираете, у вас будут проблемы. Но разве это опаснее, чем быть неаккуратным с get()?)

Ответ 1

Большинство аргументов против eval(parse(...)) возникают не из-за проблем с безопасностью, ведь претензии не предъявляются к тому, что R является безопасным интерфейсом для публикации в Интернете, а скорее потому, что такой код обычно выполняет то, что может быть выполнено используя менее неясные методы, то есть методы, которые являются более быстрыми и более человечными. Предполагается, что R-язык является высокоуровневым, поэтому предпочтение cognoscenti (и я не считаю себя в этой группе) заключается в том, чтобы видеть компактный и выразительный код.

Таким образом, опасность состоит в том, что eval(parse(..)) - это бэкдор-метод преодоления недостатка знаний, и надежда на повышение этого барьера заключается в том, что люди улучшат использование языка R. Дверь остается открытой, но надеемся на более выразительное использование других функций. вопрос Carl Witthoft ранее сегодня, не зная, что функция get доступна, и вопрос который он связал с выявлено непонимание того, как функционировала функция [[ (и как $ была более ограниченной, чем [[). В обоих случаях можно было бы построить решение eval(parse(..)), но оно было clunkier и менее понятно, чем альтернатива.

Ответ 2

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

Некоторые другие проблемы с eval(parse( хотя.

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

Здесь функция с ошибкой в ​​ней.

std <- function()
{
  mean(1to10)
}

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

Здесь версия eval-parse.

ep <- function()
{
  eval(parse(text = "mean(1to10)"))
}

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

Я также думаю, что эту вторую версию функции гораздо труднее прочитать.

Другая проблема с eval-parse заключается в том, что она намного медленнее, чем непосредственно исполняемый код. Сравнить

system.time(for(i in seq_len(1e4)) mean(1:10))
   user  system elapsed 
   0.08    0.00    0.07

и

system.time(for(i in seq_len(1e4)) eval(parse(text = "mean(1:10)")))
   user  system elapsed 
   1.54    0.14    1.69

Ответ 3

Обычно существует лучший способ "вычисления на языке", чем работа с кодовыми строками; По моему опыту, тяжелый код evalparse требует много надежной защиты, чтобы гарантировать разумную производительность.

Та же задача обычно может быть решена путем непосредственного взаимодействия с R-кодом как языкового объекта; Хэдли Уикхем имеет полезное руководство по метапрограмме в R здесь:

Функция defmacro() в библиотеке gtools - мой любимый заменитель (не предполагаемый R-кадр) для конструкции evalparse

require(gtools)

# both action_to_take & predicate will be subbed with code

F <- defmacro(predicate, action_to_take, expr = 
    if(predicate) action_to_take)

F(1 != 1, action_to_take = print('arithmetic doesnt work!'))

F(pi > 3, action_to_take = return('good!'))
[1] 'good!'

# the raw code for F
print(F)

function (predicate = stop("predicate not supplied"), action_to_take = stop("action_to_take not supplied")) 
{
    tmp <- substitute(if (predicate) action_to_take)
    eval(tmp, parent.frame())
}
<environment: 0x05ad5d3c> 

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

Надеюсь, что это поможет!

Ответ 4

В некоторых языках программирования eval() - это функция, которая оценивает строка, как будто это выражение и возвращает результат; в другие, он выполняет несколько строк кода, как если бы они были вместо строки, включая eval. Вход для eval не обязательно строка; в языках, поддерживающих синтаксические абстракции (например, Lisp), вкладка eval будет состоять из абстрактных синтаксических форм. http://en.wikipedia.org/wiki/Eval

Есть все виды эксплойтов, которые можно использовать, если eval используется неправильно.

Злоумышленник может предоставить программу с помощью строки msgstr "session.update(authenticated = True)" в качестве данных, которые будут обновлять чтобы установить аутентифицированный ключ в значение True. К исправлению это, все данные, которые будут использоваться с eval, должны быть экранированы или должен выполняться без доступа к потенциально опасным функциям. http://en.wikipedia.org/wiki/Eval

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

В частности, в R, вероятно, потому, что вы можете использовать get() вместо eval(parse()), и ваши результаты будут одинаковыми, не прибегая к eval()