Безопасное выполнение строки кода Python: альтернативы exec и eval

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для того чтобы выполнить код Python, находящийся в строке, используйте функцию exec():

Python
Скопировать код
exec('print("Здравствуйте, пользователи SO!")')  # Мы становимся магами, волшебные строки начинают исполняться!

После выполнения строка Здравствуйте, пользователи SO! отобразится на экране. Используйте эту мощную функцию с пониманием ответственности. Она может без труда открывает "ящик Пандоры" проблем безопасности при обработке ненадежных или неизвестных данных.

Кинга Идем в IT: пошаговый план для смены профессии

Взвешивая "за" и "против": использовать exec или нет

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

Если ваш выбор все же пал на exec, убедитесь в безопасности входной информации, чтобы предотвратить возможные атаки с инъекцией кода. Проверяйте и фильтруйте строки перед тем как передавать их в exec. Ниже пример того, как можно управлять переменными и результатами выполнения кода в exec:

Python
Скопировать код
from io import StringIO
import sys

code = 'print("Еще раз приветствуем пользователей SO!")' 
old_stdout = sys.stdout
new_stdout = StringIO()
sys.stdout = new_stdout

try:
    exec(code)  # Надеемся, что не случится чего-то страшного
finally:
    sys.stdout = old_stdout
    output = new_stdout.getvalue()
    new_stdout.close()

print(f'Данные после выполнения exec: {output}')

Размещая безопасное окружение: ограничение среды выполнения

Важно направлять вызовы функции exec адекватно, подобно приучению домашнего питомца к внутридомовым порядкам. Другими словами, определите ограничения для пространства имен для функции exec. Это поможет снизить риски, связанные с непреднамеренным (или преднамеренным) нарушением глобального пространства имен:

Python
Скопировать код
namespace = {}
# Ограничиваем exec его собственным "огородиком", ибо характер его непредсказуем.
exec('foo = "Это переменная foo"', namespace)
print(namespace['foo'])  # Вывод: Это переменная foo

Визуализация

Представьте выполнение строки кода на Python, как процесс заказа еды:

Markdown
Скопировать код
Строка С Кодом Python (📄): "print('Привет, мир!')"

Заказ Коду (👨‍🍳): "Подайте, пожалуйста, блюдо 'Привет, мир!'"

Компьютер исполняет роль Кухни (💻), где готовят и подают заказанное блюдо:

Python
Скопировать код
exec("print('Привет, мир!')")  # И вот повар заявляет: "Ваш заказ готов!"

И вуаля, ваше блюдо (результат 🍽️): Привет, мир!

Markdown
Скопировать код
| Сделан Заказ (📄)     | Подан Заказ (🍽️)      |
| --------------- | -------------------- |
| "print('Привет, мир!')" | "Привет, мир!" |

Ошибка эстафеты здесь — передача команды exec, ожидание и получение желаемого результата!

Поиск безопасного убежища: альтернативы exec

Меньше риска, если возможно избегать использования exec. Применение функций высшего порядка зачастую является адекватной заменой exec. Встроенные функции Python, вроде map(), filter() и reduce(), обеспечивают гибкие методы манипулирования данными со значительно более низким риском для безопасности.

Также стоит рассмотреть классы и паттерны проектирования "фабрика" — они позволяют создавать нужное динамическое поведение через наследование и переопределение методов, уклоняясь от необходимости использовать exec.

Разграничение безопасной зоны: предостережения при использовании exec

В случаях, когда использование exec неизбежно и является единственным решением, используйте safe_dict для ограничения пространства выполнения exec:

Python
Скопировать код
safe_dict = {'__builtins__': None}
exec('print("Мы в безопасности благодаря safe_dict!")', safe_dict)  # Мой exec содержится в надежном заборе

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

Полезные материалы

  1. Встроенные функции — Документация Python 3.12.2 — Официальная документация Python по exec.
  2. Python eval(): Динамическое выполнение выражений – Real Python — Урок об использовании функций exec и eval для выполнения кода Python.
  3. exec() в Python – GeeksforGeeks — Детальное описание использования exec в Python.
  4. Мощные Python One-Liners – Python Wiki — Изящное искусство написания динамически выполняемого кода в Python при помощи exec.
  5. Just a moment... — Обсуждение проблем безопасности, связанных с использованием функций exec и eval.