Безопасное выполнение строки кода Python: альтернативы exec и eval
Быстрый ответ
Для того чтобы выполнить код Python, находящийся в строке, используйте функцию exec()
:
exec('print("Здравствуйте, пользователи SO!")') # Мы становимся магами, волшебные строки начинают исполняться!
После выполнения строка Здравствуйте, пользователи SO!
отобразится на экране. Используйте эту мощную функцию с пониманием ответственности. Она может без труда открывает "ящик Пандоры" проблем безопасности при обработке ненадежных или неизвестных данных.
Взвешивая "за" и "против": использовать exec или нет
Использование функции exec()
требует ответственного подхода. Возможность выполнения динамического кода на Python безусловно заманчива, однако необходимо быть осведомленным о скрывающихся за этим серьезных безопасностных вопросах и потенциальном снижении производительности. Прежде чем прибегнуть к применению exec
, следует рассмотреть альтернативные способы, такие как функции высшего порядка, классы или модули — они способны обеспечить требуемую динамичность без рисков, связанных с exec
.
Если ваш выбор все же пал на exec
, убедитесь в безопасности входной информации, чтобы предотвратить возможные атаки с инъекцией кода. Проверяйте и фильтруйте строки перед тем как передавать их в exec
. Ниже пример того, как можно управлять переменными и результатами выполнения кода в exec
:
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
. Это поможет снизить риски, связанные с непреднамеренным (или преднамеренным) нарушением глобального пространства имен:
namespace = {}
# Ограничиваем exec его собственным "огородиком", ибо характер его непредсказуем.
exec('foo = "Это переменная foo"', namespace)
print(namespace['foo']) # Вывод: Это переменная foo
Визуализация
Представьте выполнение строки кода на Python, как процесс заказа еды:
Строка С Кодом Python (📄): "print('Привет, мир!')"
Заказ Коду (👨🍳): "Подайте, пожалуйста, блюдо 'Привет, мир!'"
Компьютер исполняет роль Кухни (💻), где готовят и подают заказанное блюдо:
exec("print('Привет, мир!')") # И вот повар заявляет: "Ваш заказ готов!"
И вуаля, ваше блюдо (результат 🍽️): Привет, мир!
| Сделан Заказ (📄) | Подан Заказ (🍽️) |
| --------------- | -------------------- |
| "print('Привет, мир!')" | "Привет, мир!" |
Ошибка эстафеты здесь — передача команды exec
, ожидание и получение желаемого результата!
Поиск безопасного убежища: альтернативы exec
Меньше риска, если возможно избегать использования exec
. Применение функций высшего порядка зачастую является адекватной заменой exec
. Встроенные функции Python, вроде map()
, filter()
и reduce()
, обеспечивают гибкие методы манипулирования данными со значительно более низким риском для безопасности.
Также стоит рассмотреть классы и паттерны проектирования "фабрика" — они позволяют создавать нужное динамическое поведение через наследование и переопределение методов, уклоняясь от необходимости использовать exec
.
Разграничение безопасной зоны: предостережения при использовании exec
В случаях, когда использование exec
неизбежно и является единственным решением, используйте safe_dict
для ограничения пространства выполнения exec
:
safe_dict = {'__builtins__': None}
exec('print("Мы в безопасности благодаря safe_dict!")', safe_dict) # Мой exec содержится в надежном заборе
Не стоит расслабляться, полагаясь на иллюзию безопасности, которую создает удаление globals
и builtins
, так как это не является эффективной помехой для инъекций кода. Указание списка разрешений обычно является более безопасным методом. Но, как всегда, возьмите на заметку — действовать с осторожностью никогда не вредит.
Полезные материалы
- Встроенные функции — Документация Python 3.12.2 — Официальная документация Python по
exec
. - Python eval(): Динамическое выполнение выражений – Real Python — Урок об использовании функций
exec
иeval
для выполнения кода Python. - exec() в Python – GeeksforGeeks — Детальное описание использования
exec
в Python. - Мощные Python One-Liners – Python Wiki — Изящное искусство написания динамически выполняемого кода в Python при помощи
exec
. - Just a moment... — Обсуждение проблем безопасности, связанных с использованием функций
exec
иeval
.