rexec.md
1> **Источник:** https://python-all.ru/2.7/library/rexec.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 30.1. [`rexec`](https://python-all.ru/2.7/library/rexec.html#module-rexec) – среда ограниченного выполнения89Устарело с версии 2.6: Модуль [`rexec`](https://python-all.ru/2.7/library/rexec.html#module-rexec) удалён в Python 3.1011Изменено в версии 2.3: Модуль отключён.1213> **Предупреждение**14>15> Документация оставлена на месте, чтобы помочь при чтении старого кода, который использует этот модуль.1617Этот модуль содержит класс [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec), который поддерживает методы `r_eval()`, `r_execfile()`, `r_exec()` и `r_import()` – ограниченные версии стандартных функций Python [`eval()`](https://python-all.ru/2.7/library/functions.html#eval), [`execfile()`](https://python-all.ru/2.7/library/functions.html#execfile), а также инструкций [`exec`](https://python-all.ru/2.7/reference/simple_stmts.html#exec) и [`import`](https://python-all.ru/2.7/reference/simple_stmts.html#import). Код, выполняемый в этой ограниченной среде, будет иметь доступ только к модулям и функциям, которые считаются безопасными; при необходимости можно создать подкласс [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec), чтобы добавить или убрать возможности.1819> **Предупреждение**20>21> Хотя модуль [`rexec`](https://python-all.ru/2.7/library/rexec.html#module-rexec) спроектирован так, чтобы работать как описано ниже, у него есть несколько известных уязвимостей, которые можно использовать с помощью тщательно написанного кода. Поэтому на него не стоит полагаться в ситуациях, требующих «промышленной» безопасности. В таких случаях может потребоваться выполнение через подпроцессы или очень тщательная «очистка» как кода, так и обрабатываемых данных. Альтернативно, помощь в исправлении известных уязвимостей [`rexec`](https://python-all.ru/2.7/library/rexec.html#module-rexec) приветствуется.2223> **Примечание**24>25> Класс [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) может предотвратить выполнение кодом небезопасных операций, таких как чтение или запись файлов на диске, или использование TCP/IP-сокетов. Однако он не защищает от кода, использующего чрезмерно большие объёмы памяти или процессорного времени.2627#### `class rexec.RExec([hooks[, verbose]])`2829Возвращает экземпляр класса [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec).3031*hooks* – это экземпляр класса `RHooks` или его подкласса. Если он опущен или равен `None`, создаётся экземпляр класса `RHooks` по умолчанию. Когда модуль [`rexec`](https://python-all.ru/2.7/library/rexec.html#module-rexec) ищет модуль (даже встроенный) или читает код модуля, он на самом деле не обращается к файловой системе самостоятельно. Вместо этого он вызывает методы экземпляра `RHooks`, который был передан конструктору или создан им. (На самом деле объект [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) не делает эти вызовы – их выполняет объект загрузчика модулей, входящий в состав объекта [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec). Это добавляет дополнительный уровень гибкости, что может быть полезно при изменении механики [`import`](https://python-all.ru/2.7/reference/simple_stmts.html#import) в рамках ограниченной среды.)3233Предоставляя альтернативный объект `RHooks`, можно контролировать доступы к файловой системе, необходимые для импорта модуля, не меняя сам алгоритм, определяющий порядок этих доступов. Например, можно подставить объект `RHooks`, который перенаправляет все файловые запросы на удалённый файловый сервер через какой-нибудь RPC-механизм, вроде ILU. Загрузчик апплетов Grail использует это для поддержки импорта апплетов из URL-адреса каталога.3435Если *verbose* равен true, дополнительная отладочная информация может выводиться на стандартный вывод.3637Важно понимать, что код, выполняемый в ограниченной среде, всё равно может вызвать функцию [`sys.exit()`](https://python-all.ru/2.7/library/sys.html#sys.exit). Чтобы запретить ограниченному коду завершать интерпретатор, всегда защищайте вызовы, приводящие к выполнению такого кода, с помощью инструкции [`try`](https://python-all.ru/2.7/reference/compound_stmts.html#try)/[`except`](https://python-all.ru/2.7/reference/compound_stmts.html#except), перехватывающей исключение [`SystemExit`](https://python-all.ru/2.7/library/exceptions.html#exceptions.SystemExit). Удаление функции [`sys.exit()`](https://python-all.ru/2.7/library/sys.html#sys.exit) из ограниченной среды недостаточно – ограниченный код всё равно может использовать `raise SystemExit`. Удаление [`SystemExit`](https://python-all.ru/2.7/library/exceptions.html#exceptions.SystemExit) – неразумный вариант; некоторые библиотеки используют её, и без неё они сломаются.3839> **См. также**40>41> **[Домашняя страница Grail](https://python-all.ru/2.7/library/rexec.html)**42>43> Grail – это веб-браузер, полностью написанный на Python. Он использует модуль [`rexec`](https://python-all.ru/2.7/library/rexec.html#module-rexec) как основу для поддержки апплетов Python и может служить примером использования этого модуля.4445## 30.1.1. Объекты RExec4647Экземпляры [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) поддерживают следующие методы:4849#### `RExec.r_eval(code)`5051*code* должен быть либо строкой, содержащей выражение Python, либо скомпилированным объектом кода, который будет вычислен в модуле [`__main__`](https://python-all.ru/2.7/library/__main__.html#module-__main__) ограниченной среды. Будет возвращено значение выражения или объекта кода.5253#### `RExec.r_exec(code)`5455*code* должен быть либо строкой, содержащей одну или несколько строк кода Python, либо скомпилированным объектом кода, который будет выполнен в модуле [`__main__`](https://python-all.ru/2.7/library/__main__.html#module-__main__) ограниченной среды.5657#### `RExec.r_execfile(filename)`5859Выполнить код Python из файла *filename* в модуле [`__main__`](https://python-all.ru/2.7/library/__main__.html#module-__main__) ограниченной среды.6061Методы, чьи имена начинаются с `s_`, аналогичны функциям, начинающимся с `r_`, но код будет иметь доступ к ограниченным версиям стандартных потоков ввода-вывода `sys.stdin`, `sys.stderr` и `sys.stdout`.6263#### `RExec.s_eval(code)`6465*code* должен быть строкой, содержащей выражение Python, которое будет вычислено в ограниченной среде.6667#### `RExec.s_exec(code)`6869*code* должен быть строкой, содержащей одну или несколько строк кода Python, который будет выполнен в ограниченной среде.7071#### `RExec.s_execfile(code)`7273Выполнить код Python из файла *filename* в ограниченной среде.7475Объекты [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) также должны поддерживать различные методы, которые будут неявно вызываться кодом, выполняющимся в ограниченной среде. Переопределение этих методов в подклассе используется для изменения политик, применяемых ограниченной средой.7677#### `RExec.r_import(modulename[, globals[, locals[, fromlist]]])`7879Импортирует модуль *modulename*, вызывая исключение [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError), если модуль считается небезопасным.8081#### `RExec.r_open(filename[, mode[, bufsize]])`8283Метод, вызываемый при вызове [`open()`](https://python-all.ru/2.7/library/functions.html#open) в ограниченной среде. Аргументы идентичны аргументам [`open()`](https://python-all.ru/2.7/library/functions.html#open), и должен быть возвращён файловый объект (или экземпляр класса, совместимый с файловыми объектами). Поведение [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) по умолчанию разрешает открытие любого файла для чтения, но запрещает любую попытку записи в файл. См. пример ниже для реализации менее строгой [`r_open()`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec.r_open).8485#### `RExec.r_reload(module)`8687Перезагружает объект модуля *module*, повторно разбирая и инициализируя его.8889#### `RExec.r_unload(module)`9091Выгружает объект модуля *module* (удаляет его из словаря `sys.modules` ограниченной среды).9293И их эквиваленты с доступом к ограниченным стандартным потокам ввода-вывода:9495#### `RExec.s_import(modulename[, globals[, locals[, fromlist]]])`9697Импортирует модуль *modulename*, вызывая исключение [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError), если модуль считается небезопасным.9899#### `RExec.s_reload(module)`100101Перезагружает объект модуля *module*, повторно разбирая и инициализируя его.102103#### `RExec.s_unload(module)`104105Выгрузить объект модуля *module*.106107## 30.1.2. Определение ограниченных сред выполнения108109Класс [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) имеет следующие атрибуты класса, используемые методом [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__). Изменение их в существующем экземпляре не даст никакого эффекта; вместо этого создайте подкласс [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) и назначьте им новые значения в определении класса. Экземпляры нового класса будут использовать эти новые значения. Все эти атрибуты – кортежи строк.110111#### `RExec.nok_builtin_names`112113Содержит имена встроенных функций, которые будут *не*доступны программам, выполняющимся в ограниченной среде. Значение для [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) равно `('open', 'reload', '__import__')`. (Это даёт исключения, поскольку подавляющее большинство встроенных функций безвредны. Подкласс, который хочет переопределить эту переменную, вероятно, должен начать со значения базового класса и добавить дополнительные запрещённые функции – когда в Python добавляются новые опасные встроенные функции, они также будут добавлены в этот модуль.)114115#### `RExec.ok_builtin_modules`116117Содержит имена встроенных модулей, которые можно безопасно импортировать. Значение для [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) равно `('audioop', 'array', 'binascii', 'cmath', 'errno', 'imageop', 'marshal', 'math', 'md5', 'operator', 'parser', 'regex', 'select', 'sha', '_sre', 'strop', 'struct', 'time')`. Аналогичное замечание о переопределении этой переменной применимо – используйте значение базового класса в качестве отправной точки.118119#### `RExec.ok_path`120121Содержит каталоги, которые будут просматриваться при выполнении [`import`](https://python-all.ru/2.7/reference/simple_stmts.html#import) в ограниченной среде. Значение для [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) совпадает с `sys.path` (на момент загрузки модуля) для неограниченного кода.122123#### `RExec.ok_posix_names`124125Содержит имена функций в модуле [`os`](https://python-all.ru/2.7/library/os.html#module-os), которые будут доступны программам, выполняющимся в ограниченной среде. Значение для [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) равно `('error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat', 'times', 'uname', 'getpid', 'getppid', 'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')`.126127#### `RExec.ok_sys_names`128129Содержит имена функций и переменных в модуле [`sys`](https://python-all.ru/2.7/library/sys.html#module-sys), которые будут доступны программам, выполняющимся в ограниченной среде. Значение для [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) равно `('ps1', 'ps2', 'copyright', 'version', 'platform', 'exit', 'maxint')`.130131#### `RExec.ok_file_types`132133Содержит типы файлов, из которых разрешена загрузка модулей. Каждый тип файла – целая константа, определённая в модуле [`imp`](https://python-all.ru/2.7/library/imp.html#module-imp). Значимые значения: `PY_SOURCE`, `PY_COMPILED` и `C_EXTENSION`. Значение для [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec) равно `(C_EXTENSION, PY_SOURCE)`. Добавление `PY_COMPILED` в подклассы не рекомендуется; злоумышленник может выйти из режима ограниченного выполнения, поместив поддельный байт-компилированный файл (`.pyc`) в любом месте вашей файловой системы, например записав его в `/tmp` или загрузив в каталог `/incoming` вашего общедоступного FTP-сервера.134135## 30.1.3. Пример136137Допустим, мы хотим немного более мягкую политику, чем стандартный класс [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec). Например, если мы готовы разрешить запись файлов в `/tmp`, мы можем создать подкласс класса [`RExec`](https://python-all.ru/2.7/library/rexec.html#rexec.RExec):138139```python140class TmpWriterRExec(rexec.RExec):141 def r_open(self, file, mode='r', buf=-1):142 if mode in ('r', 'rb'):143 pass144 elif mode in ('w', 'wb', 'a', 'ab'):145 # проверить имя файла: должно начинаться с /tmp/146 if file[:5]!='/tmp/':147 raise IOError("can't write outside /tmp")148 elif (string.find(file, '/../') >= 0 or149 file[:3] == '../' or file[-3:] == '/..'):150 raise IOError("'..' in filename forbidden")151 else: raise IOError("Illegal open() mode")152 return open(file, mode, buf)153```154155Обратите внимание, что приведённый выше код иногда будет запрещать вполне допустимое имя файла; например, код в ограниченной среде не сможет открыть файл с именем `/tmp/foo/../bar`. Чтобы исправить это, методу `r_open()` пришлось бы упростить имя файла до `/tmp/bar`, что потребовало бы разбиения имени файла и выполнения различных операций. В случаях, когда безопасность стоит на кону, может быть предпочтительнее написать простой код, который иногда чрезмерно ограничивает, вместо более общего кода, который также более сложен и может содержать скрытые уязвимости.156