Подтвердить что ты не робот

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

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

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

EDIT: Я вижу, что люди предполагают, что этот вопрос относится к веб-серверам - это не так. Я могу понять, почему несанкционированная строка, передаваемая в exec, может быть плохим. Это плохо в не-веб-приложениях?

4b9b3361

Ответ 1

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

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

Я использовал 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() ok для небольших задач, тестов и действительно временных вещей, но bad для постоянного использования, потому что почти всегда есть лучший способ сделай это!

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

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

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