Содержание страницы
30.1. rexec – среда ограниченного выполнения¶rexec – Restricted execution framework
Устарело с версии 2.6: Модуль rexec удалён в Python 3.
Изменено в версии 2.3: Модуль отключён.
Предупреждение
Документация оставлена на месте, чтобы помочь при чтении старого кода, который использует этот модуль.
Этот модуль содержит класс RExec, который поддерживает методы r_eval(),
r_execfile(), r_exec() и r_import() – ограниченные версии стандартных функций Python eval(),
execfile(), а также инструкций exec и import. Код, выполняемый в этой ограниченной среде, будет иметь доступ только к модулям и функциям, которые считаются безопасными; при необходимости можно создать подкласс RExec, чтобы добавить или убрать возможности.
Предупреждение
Хотя модуль rexec спроектирован так, чтобы работать как описано ниже, у него есть несколько известных уязвимостей, которые можно использовать с помощью тщательно написанного кода. Поэтому на него не стоит полагаться в ситуациях, требующих «промышленной» безопасности. В таких случаях может потребоваться выполнение через подпроцессы или очень тщательная «очистка» как кода, так и обрабатываемых данных. Альтернативно, помощь в исправлении известных уязвимостей rexec приветствуется.
Примечание
Класс RExec может предотвратить выполнение кодом небезопасных операций, таких как чтение или запись файлов на диске, или использование TCP/IP-сокетов. Однако он не защищает от кода, использующего чрезмерно большие объёмы памяти или процессорного времени.
-
class
rexec.RExec([hooks[, verbose]])¶ Возвращает экземпляр класса
RExec.hooks – это экземпляр класса
RHooksили его подкласса. Если он опущен или равенNone, создаётся экземпляр классаRHooksпо умолчанию. Когда модульrexecищет модуль (даже встроенный) или читает код модуля, он на самом деле не обращается к файловой системе самостоятельно. Вместо этого он вызывает методы экземпляраRHooks, который был передан конструктору или создан им. (На самом деле объектRExecне делает эти вызовы – их выполняет объект загрузчика модулей, входящий в состав объектаRExec. Это добавляет дополнительный уровень гибкости, что может быть полезно при изменении механикиimportв рамках ограниченной среды.)Предоставляя альтернативный объект
RHooks, можно контролировать доступы к файловой системе, необходимые для импорта модуля, не меняя сам алгоритм, определяющий порядок этих доступов. Например, можно подставить объектRHooks, который перенаправляет все файловые запросы на удалённый файловый сервер через какой-нибудь RPC-механизм, вроде ILU. Загрузчик апплетов Grail использует это для поддержки импорта апплетов из URL-адреса каталога.Если verbose равен true, дополнительная отладочная информация может выводиться на стандартный вывод.
Важно понимать, что код, выполняемый в ограниченной среде, всё равно может вызвать функцию sys.exit(). Чтобы запретить ограниченному коду завершать интерпретатор, всегда защищайте вызовы, приводящие к выполнению такого кода, с помощью инструкции try/except, перехватывающей исключение SystemExit. Удаление функции sys.exit() из ограниченной среды недостаточно – ограниченный код всё равно может использовать raise SystemExit. Удаление SystemExit – неразумный вариант; некоторые библиотеки используют её, и без неё они сломаются.
См. также
- Домашняя страница Grail
Grail – это веб-браузер, полностью написанный на Python. Он использует модуль
rexecкак основу для поддержки апплетов Python и может служить примером использования этого модуля.
30.1.1. Объекты RExec¶RExec Objects
Экземпляры RExec поддерживают следующие методы:
-
RExec.r_eval(code)¶ code должен быть либо строкой, содержащей выражение Python, либо скомпилированным объектом кода, который будет вычислен в модуле
__main__ограниченной среды. Будет возвращено значение выражения или объекта кода.
-
RExec.r_exec(code)¶ code должен быть либо строкой, содержащей одну или несколько строк кода Python, либо скомпилированным объектом кода, который будет выполнен в модуле
__main__ограниченной среды.
-
RExec.r_execfile(filename)¶ Выполнить код Python из файла filename в модуле
__main__ограниченной среды.
Методы, чьи имена начинаются с s_, аналогичны функциям, начинающимся с r_, но код будет иметь доступ к ограниченным версиям стандартных потоков ввода-вывода sys.stdin, sys.stderr и sys.stdout.
-
RExec.s_eval(code)¶ code должен быть строкой, содержащей выражение Python, которое будет вычислено в ограниченной среде.
-
RExec.s_exec(code)¶ code должен быть строкой, содержащей одну или несколько строк кода Python, который будет выполнен в ограниченной среде.
-
RExec.s_execfile(code)¶ Выполнить код Python из файла filename в ограниченной среде.
Объекты RExec также должны поддерживать различные методы, которые будут неявно вызываться кодом, выполняющимся в ограниченной среде. Переопределение этих методов в подклассе используется для изменения политик, применяемых ограниченной средой.
-
RExec.r_import(modulename[, globals[, locals[, fromlist]]])¶ Импортирует модуль modulename, вызывая исключение
ImportError, если модуль считается небезопасным.
-
RExec.r_open(filename[, mode[, bufsize]])¶ Метод, вызываемый при вызове
open()в ограниченной среде. Аргументы идентичны аргументамopen(), и должен быть возвращён файловый объект (или экземпляр класса, совместимый с файловыми объектами). ПоведениеRExecпо умолчанию разрешает открытие любого файла для чтения, но запрещает любую попытку записи в файл. См. пример ниже для реализации менее строгойr_open().
-
RExec.r_reload(module)¶ Перезагружает объект модуля module, повторно разбирая и инициализируя его.
-
RExec.r_unload(module)¶ Выгружает объект модуля module (удаляет его из словаря
sys.modulesограниченной среды).
И их эквиваленты с доступом к ограниченным стандартным потокам ввода-вывода:
-
RExec.s_import(modulename[, globals[, locals[, fromlist]]])¶ Импортирует модуль modulename, вызывая исключение
ImportError, если модуль считается небезопасным.
-
RExec.s_reload(module)¶ Перезагружает объект модуля module, повторно разбирая и инициализируя его.
-
RExec.s_unload(module)¶ Выгрузить объект модуля module.
30.1.2. Определение ограниченных сред выполнения¶Defining restricted environments
Класс RExec имеет следующие атрибуты класса, используемые
методом __init__(). Изменение их в существующем экземпляре не даст
никакого эффекта; вместо этого создайте подкласс RExec и назначьте им новые
значения в определении класса. Экземпляры нового класса будут использовать эти
новые значения. Все эти атрибуты – кортежи строк.
-
RExec.nok_builtin_names¶ Содержит имена встроенных функций, которые будут недоступны программам, выполняющимся в ограниченной среде. Значение для
RExecравно('open', 'reload', '__import__'). (Это даёт исключения, поскольку подавляющее большинство встроенных функций безвредны. Подкласс, который хочет переопределить эту переменную, вероятно, должен начать со значения базового класса и добавить дополнительные запрещённые функции – когда в Python добавляются новые опасные встроенные функции, они также будут добавлены в этот модуль.)
-
RExec.ok_builtin_modules¶ Содержит имена встроенных модулей, которые можно безопасно импортировать. Значение для
RExecравно('audioop', 'array', 'binascii', 'cmath', 'errno', 'imageop', 'marshal', 'math', 'md5', 'operator', 'parser', 'regex', 'select', 'sha', '_sre', 'strop', 'struct', 'time'). Аналогичное замечание о переопределении этой переменной применимо – используйте значение базового класса в качестве отправной точки.
-
RExec.ok_path¶ Содержит каталоги, которые будут просматриваться при выполнении
importв ограниченной среде. Значение дляRExecсовпадает сsys.path(на момент загрузки модуля) для неограниченного кода.
-
RExec.ok_posix_names¶ Содержит имена функций в модуле
os, которые будут доступны программам, выполняющимся в ограниченной среде. Значение дляRExecравно('error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat', 'times', 'uname', 'getpid', 'getppid', 'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid').
-
RExec.ok_sys_names¶ Содержит имена функций и переменных в модуле
sys, которые будут доступны программам, выполняющимся в ограниченной среде. Значение дляRExecравно('ps1', 'ps2', 'copyright', 'version', 'platform', 'exit', 'maxint').
-
RExec.ok_file_types¶ Содержит типы файлов, из которых разрешена загрузка модулей. Каждый тип файла – целая константа, определённая в модуле
imp. Значимые значения:PY_SOURCE,PY_COMPILEDиC_EXTENSION. Значение дляRExecравно(C_EXTENSION, PY_SOURCE). ДобавлениеPY_COMPILEDв подклассы не рекомендуется; злоумышленник может выйти из режима ограниченного выполнения, поместив поддельный байт-компилированный файл (.pyc) в любом месте вашей файловой системы, например записав его в/tmpили загрузив в каталог/incomingвашего общедоступного FTP-сервера.
30.1.3. Пример¶An example
Допустим, мы хотим немного более мягкую политику, чем стандартный
класс RExec. Например, если мы готовы разрешить запись файлов в
/tmp, мы можем создать подкласс класса RExec:
class TmpWriterRExec(rexec.RExec):
def r_open(self, file, mode='r', buf=-1):
if mode in ('r', 'rb'):
pass
elif mode in ('w', 'wb', 'a', 'ab'):
# проверить имя файла: должно начинаться с /tmp/
if file[:5]!='/tmp/':
raise IOError("can't write outside /tmp")
elif (string.find(file, '/../') >= 0 or
file[:3] == '../' or file[-3:] == '/..'):
raise IOError("'..' in filename forbidden")
else: raise IOError("Illegal open() mode")
return open(file, mode, buf)
Обратите внимание, что приведённый выше код иногда будет запрещать вполне допустимое имя файла;
например, код в ограниченной среде не сможет открыть файл
с именем /tmp/foo/../bar. Чтобы исправить это, методу r_open() пришлось бы
упростить имя файла до /tmp/bar, что потребовало бы разбиения
имени файла и выполнения различных операций. В случаях, когда
безопасность стоит на кону, может быть предпочтительнее написать простой код, который
иногда чрезмерно ограничивает, вместо более общего кода, который также более
сложен и может содержать скрытые уязвимости.