Почему следует избегать exec () и eval ()?

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

Так что, надеюсь, здесь будет представлен. Почему мы должны (по крайней мере, вообще) не использовать exec() и eval() ?

  • Вызов функции модуля из строки с именем функции
  • Глобальная переменная из другого файла Python
  • Возвращаемый список элементов в списке больше некоторого значения
  • Исправлено для python
  • Почему «if not someobj:» лучше, чем «if someobj == None:» в Python?
  • Печать python после выполнения
  • EDIT: Я вижу, что люди предполагают, что этот вопрос относится к веб-серверам – это не так. Я могу понять, почему несанкционированная строка, передаваемая exec может быть плохим. Это плохо в не-веб-приложениях?

  • Как преобразовать кортеж в строку значений без запятой и круглых скобок
  • Невозможно обработать DeadlineExceededError при использовании UrlFetch
  • Как прочитать текстовый файл в строковой переменной в Python
  • разбор сложного логического выражения в pyparsing в двоичном древе
  • Как получить отличное значение одной из моих моделей в Google App Engine
  • Частные функции / Применения переменных в python
  • 11 Solutions collect form web for “Почему следует избегать exec () и eval ()?”

    Часто есть более четкие, более прямые способы получить тот же эффект. Если вы построите сложную строку и передаете ее в exec, код трудно выполнить и трудно проверить.

    Пример: я написал код, который читается в строковых клавишах и значениях и задает соответствующие поля в объекте. Это выглядело так:

     for key, val in values: fieldName = valueToFieldName[key] fieldType = fieldNameToType[fieldName] if fieldType is int: s = 'object.%s = int(%s)' % (fieldName, fieldType) #Many clauses like this... exec(s) 

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

    Первое правило четкости кода заключается в том, что каждая строка вашего кода должна быть легко понятна, если посмотреть только на близлежащие строки. Вот почему goto и глобальные переменные обескуражены. exec и eval упрощают нарушение этого правила.

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

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

    Тем не менее, как и все обобщения, будьте осторожны с этим. В некоторых случаях exec и eval могут быть полезными. Но у вас должна быть очень веская причина для их использования. См. Это сообщение для одного приемлемого использования.

    eval () и exec () могут способствовать ленивому программированию. Что еще более важно, это означает, что выполняемый код не может быть написан во время разработки, поэтому не проверен. Другими словами, как вы тестируете динамически сгенерированный код? Особенно в браузерах.

    Когда вам нужен exec и eval, да, вы действительно им нужны.

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

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

    Это приманка струн. Бросание сегментов строк вокруг выглядит легко и обманывает наивных кодеров, думая, что они понимают, что они делают. Но опыт показывает, что результаты почти всегда ошибочны в некоторых случаях (или не в таком углу), часто с потенциальными последствиями для безопасности. Вот почему мы говорим, что eval – это зло. Вот почему мы говорим, что regex-for-HTML – это зло. Вот почему мы запускаем параметризацию SQL. Да, вы можете получить все это правильно с ручной обработкой строк … но если вы уже не поймете, почему мы говорим об этом, скорее всего, вы этого не сделаете.

    В отличие от того, что говорит большинство ответов, exec на самом деле является частью рецепта для создания супер-полных декораторов в Python, так как вы можете точно дублировать все, что касается оформленной функции, создавая ту же подпись для целей документации и т. Д. Это ключ к функциональности широко используемого модуля декоратора ( http://pypi.python.org/pypi/decorator/ ). Другие случаи, когда exec / eval имеют важное значение, – это при создании любого типа типа «интерпретируемого Python», такого как язык шаблонов, обработанный Python (например, Mako или Jinja).

    Таким образом, это не похоже на то, что наличие этих функций является непосредственным знаком «небезопасного» приложения или библиотеки. Используя их в наивном javascripty способе оценить входящий JSON или что-то еще, да, это очень неуверенно. Но, как всегда, все это в том, как вы его используете, и это очень важные функции.

    Я использовал eval() в прошлом (и все еще делаю время от времени) для массирования данных во время быстрых и грязных операций. Это часть инструментария, которая может использоваться для выполнения заданий, но НИКОГДА не должна использоваться для всего, что вы планируете использовать в производстве, например, любых инструментов командной строки или скриптов из-за всех причин, упомянутых в других ответах.

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

    Прекрасным примером этого будет использование Django при построении QuerySet . Параметры, переданные запросу, принимают аргументы ключевых слов, которые выглядят примерно так:

     results = Foo.objects.filter(whatever__contains='pizza') 

    Если вы программно назначаете аргументы, вы можете сделать что-то вроде этого:

     results = eval("Foo.objects.filter(%s__%s=%s)" % (field, matcher, value)) 

    Но всегда есть лучший способ, который не использует eval() , который передает словарь по ссылке:

     results = Foo.objects.filter( **{'%s__%s' % (field, matcher): value} ) 

    Делая это таким образом, это не только более быстрый по производительности, но и более безопасный и более Pythonic.

    Мораль истории?

    Использование eval() подходит для небольших задач, тестов и действительно временных вещей, но плохо для постоянного использования, потому что почти всегда есть лучший способ сделать это!

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

    По той же причине вы не должны входить в систему под именем root: слишком легко стрелять в ногу.

     s = "import shutil; shutil.rmtree('/nonexisting')" eval(s) 

    Теперь предположим, что кто-то может управлять s из веб-приложения, например.

    Не пытайтесь делать это на своем компьютере

    Причина № 1: Одна ошибка безопасности (то есть ошибки программирования … и мы не можем требовать, чтобы их можно было избежать), и вы только что предоставили пользователю доступ к оболочке сервера.

    Попробуйте это в интерактивном интерпретаторе и посмотрите, что произойдет:

     >>> import sys >>> eval('{"name" : %s}' % ("sys.exit(1)")) 

    Конечно, это угловой случай, но это может быть сложно предотвратить такие вещи.

    Python - лучший язык программирования в мире.