Почему следует избегать exec () и eval ()? - PullRequest
26 голосов
/ 19 декабря 2009

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

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

РЕДАКТИРОВАТЬ: я вижу, что люди предполагают, что этот вопрос относится к веб-серверам - это не так. Я понимаю, почему передача неанизированной строки в exec может быть плохой. Это плохо в не-веб-приложениях?

Ответы [ 11 ]

19 голосов
/ 19 декабря 2009

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

13 голосов
/ 19 декабря 2009

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

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

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

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

11 голосов
/ 19 декабря 2009

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

10 голосов
/ 19 декабря 2009

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

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

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

8 голосов
/ 20 декабря 2009

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

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

6 голосов
/ 19 декабря 2009

Я использовал 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} ) 

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

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

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

5 голосов
/ 19 декабря 2009

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

3 голосов
/ 19 декабря 2009

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

3 голосов
/ 19 декабря 2009
s = "import shutil; shutil.rmtree('/nonexisting')"
eval(s)

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

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

2 голосов
/ 19 декабря 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...