http://habrahabr.ru/post/221937/
      Сколько было сломано копий при обсуждении вопроса «Возможно ли сделать 
eval безопасным?» — невозможно сосчитать. Всегда находится кто-то, кто утверждает, что нашёл способ оградиться от всех возможных последствий выполнения этой функции.
Когда мне понадобилось найти развёрнутый ответ на этот вопрос, я наткнулся на один пост. Меня приятно удивила глубина исследования, так что я решил, что это стоит перевести.
Коротко о проблеме
В Python есть встроенная функция 
eval(), которая выполняет строку с кодом и возвращает результат выполнения:
assert eval("2 + 3 * len('hello')") == 17
Это очень мощная, но в то же время и очень опасная инструкция, особенно если строки, которые вы передаёте в 
eval, получены не из доверенного источника. Что будет, если строкой, которую мы решим скормить 
eval'у, окажется 
os.system('rm -rf /')? Интерпретатор честно запустит процесс удаления всех данных с компьютера, и хорошо ещё, если он будет выполняться от имени наименее привилегированного пользователя (в последующих примерах я буду использовать 
clear (
cls, если вы используете Windows) вместо 
rm -rf /, чтобы никто из читателей случайно не 
выстрелил себе в ногу).
Какие есть решения?
Некоторые утверждают, что возможно сделать 
eval безопасным, если запускать его без доступа к символам из 
globals. В качестве второго (опционального) аргумента 
eval() принимает словарь, который будет использован вместо глобального пространства имён (все классы, методы, переменные и пр., объявленные на «верхнем» уровне, доступные из любой точки кода) кодом, который будет выполнен 
eval'ом. Если 
eval вызывается без этого аргумента, он использует текущее глобальное пространство имён, в которое мог быть импортирован модуль 
os. Если же передать пустой словарь, глобальное пространство имён для 
eval'а будет пустым. Вот такой код уже не сможет выполниться и возбудит исключение 
NameError: name 'os' is not defined:
eval("os.system('clear')", {})
Однако мы всё ещё можем импортировать модули и обращаться к ним, используя встроенную функцию 
__import__. Так, код ниже отработает без ошибок:
eval("__import__('os').system('clear')", {})
Следующей попыткой обычно становится решение запретить доступ к 
__builtins__ изнутри 
eval'a, так как имена, подобные __import__, доступны нам потому, что они находятся в глобальной переменной 
__builtins__. Если мы явно передадим вместо неё пустой словарь, код ниже уже не сможет быть выполнен:
eval("__import__('os').system('clear')", {'__builtins__':{}}) # NameError: name '__import__' is not defined
Ну а теперь-то мы в безопасности?
Некоторые говорят, что «
да» и совершают ошибку. Для примера, вот этот небольшой кусок кода вызовет 
segfault, если вы запустите его в CPython:
s = """
(lambda fc=(
    lambda n: [
        c for c in 
            ().__class__.__bases__[0].__subclasses__() 
            if c.__name__ == n
        ][0]
    ):
    fc("function")(
        fc("code")(
            0,0,0,0,"KABOOM",(),(),(),"","",0,""
        ),{}
    )()
)()
"""
eval(s, {'__builtins__':{}})
Итак, давайте разберёмся, что же здесь происходит. Начнём с этого:
().__class__.__bases__[0]
Как многие могли догадаться, это просто один из способов обратиться к 
object. Мы не можем просто написать 
object, так как 
__builtins__ пусты, но мы можем создать пустой кортеж (тьюпл), первым базовым классом которого является 
object и, пройдясь по его свойствам, получить доступ к классу 
object.
Теперь мы получаем список всех классов, которые наследуют 
object или, иными словами, список всех классов, объявленных в программе на данный момент:
().__class__.__bases__[0].__subclasses__() 
Если заменить для удобочитаемости это выражение на 
ALL_CLASSES, нетрудно будет заметить, что выражение ниже находит класс по его имени:
[c for c in ALL_CLASSES if c.__name__ == n][0]
Далее в коде нам надо будет дважды искать класс, так что создадим функцию:
lambda n: [c for c in ALL_CLASSES if c.__name__ == n][0]
Чтобы вызвать функцию, надо как-то её назвать, но, так как мы будем выполнять этот код внутри 
eval'a, мы не можем ни объявить функцию (используя 
def), ни использовать оператор присвоения, чтобы привязать нашу лямбду к какой-нибудь переменной.
Однако, есть и третий вариант: параметры по умолчанию. При объявлении лямбды, как и при объявлении любой обычной функции, мы можем задать параметры по умолчанию, так что если мы поместим весь код внутри ещё одной лямбды, и зададим ей нашу, как параметр по умолчанию, — мы добьёмся желаемого:
(lambda fc=(
    lambda n: [
        c for c in ALL_CLASSES if c.__name__ == n
        ][0]
    ):
    # теперь мы можем обращаться к нашей лямбде через fc
)()
Итак, мы имеем функцию, которая умеет искать классы, и можем обращаться к ней по имени. Что дальше? Мы создадим объект класса 
code (внутренний класс, его экземпляром, например, 
является свойство 
func_code объекта функции):
fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,"")
Из всех инициализующих параметров нас интересует только «KABOOM». Это и есть последовательность байт-кодов, которую будет использовать наш объект, и, как вы уже могли догадаться, эта последовательность не является «хорошей». На самом деле любого байт-кода из неё хватило бы, так как всё это — бинарные операторы, которые будут вызваны при пустом стеке, что приведёт к 
segfault'у CPython. "
KABOOM" 
просто выглядит забавнее, спасибо lvh за этот пример.
Итак, у нас есть объект класса 
code, но напрямую выполнить его мы не можем. Тогда создадим функцию, кодом которой и будет наш объект:
fc("function")(CODE_OBJECT, {})
Ну и теперь, когда у нас есть функция, мы можем её выполнить. Конкретно эта функция попытается выполнить наш некорректно составленный байт-код и приведёт к краху интерпретатора.
Вот весь код ещё раз:
(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]):
    fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),{})()
)()
Заключение
Итак, надеюсь теперь ни у кого не осталось сомнений в том, что 
eval НЕ БЕЗОПАСЕН, даже если убрать доступ к глобальным и встроенным переменным.
В примере выше мы использовали список всех подклассов класса 
object, чтобы создать объекты классов 
code и 
function. Точно таким же образом можно получить (и инстанцировать) любой класс, существующий в программе на момент вызова 
eval().
Вот ещё один пример того, что можно сделать:
s = """
[
    c for c in 
    ().__class__.__bases__[0].__subclasses__() 
    if c.__name__ == "Quitter"
][0](0)()
"""
eval(s, {'__builtins__':{}})
Модуль 
lib/site.py содержит класс 
Quitter, который вызывается интерпретатором, когда вы набираете 
quit().
Код выше находит этот класс, инстанциирует его и вызывает, чем завершает работу интерпретатора.
Сейчас мы запускали 
eval в пустом окружении, исходя из того, что указанный в статье код — это весь код нашей программы. 
В случае использования 
eval'а в реальном приложении злоумышленник может получить доступ ко всем классам, которые вы используете, так что его возможности не будут ограничены практически ничем.
Проблема всех подобных попыток сделать 
eval безопасным в том, что они все основаны на идее «чёрных списков», идее о том, что надо убрать доступ ко всем вещам, которые, как нам кажется, могут быть опасны при использовании в 
eval'е. С такой стратегией практически нет шансов на победу, ведь если окажется незапрещённым хоть что-то, система будет уязвима.
Когда я проводил исследование этой темы, я наткнулся на защищенный режим выполнения 
eval'а в Python, который является ещё одной попыткой побороть эту проблему:
>>> eval("(lambda:0).func_code", {'__builtins__':{}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
RuntimeError: function attributes not accessible in restricted mode
Вкратце, он работает следующим образом: если 
__builtins__ внутри 
eval отличаются от «официальных» — eval переходит в защищенный режим, в котором закрыт доступ к некоторым опасным свойствам, таким как 
func_code у функций. Более подробное описание этого режима можно найти 
тут, но, как мы уже 
видели выше, он тоже не является «серебряной пулей».
И всё-таки, можно ли сделать 
eval безопасным? Сложно сказать. Как мне кажется, злоумышленнику не удастся навредить без доступа к объектам с двумя нижними подчёркиваниями, обрамляющими имя, так что 
возможно, если исключить из обработки все строки с двумя нижними подчёркиваниями, то мы будем в безопасности. 
Возможно...P.S.
В 
треде на Reddit я нашёл короткий сниппет, позволяющий нам в eval получить «оригинальные» __builtins__:
[
    c for c in ().__class__.__base__.__subclasses__() 
    if c.__name__ == 'catch_warnings'
][0]()._module.__builtins__
Традиционное P.P.S. для хабра: прошу обо всех ошибках, неточностях и опечатках писать в личку :)