rexec
rexec - 受限制的执行框架
自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
类的一个实例。
钩子
是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编写的Web浏览器。它使用该rexec
模块作为支持Python小程序的基础,并可用作此模块的示例用法。
1. RExec对象
RExec
实例支持以下方法:
RExec.r_eval(code)
代码
必须是包含Python表达式的字符串,或者是将在受限制环境的__main__
模块中进行评估的编译代码
对象。表达式或代码
对象的值将被返回。
RExec.r_exec(code)
代码
必须是包含一行或多行Python代码
的字符串,或者是将在受限制环境的__main__
模块中执行的编译代码
对象。
RExec.r_execfile(filename)
在受限制环境的模块中执行包含在文件filename
中的Python代码__main__
。
方法名称开头s_
是类似于开头的功能r_
,但代码将被授予访问I / O流的标准的限制版本sys.stdin
,sys.stderr
和sys.stdout
。
RExec.s_eval(code)
代码
必须是包含Python表达式的字符串,它将在受限制的环境中进行评估。
RExec.s_exec(code)
代码
必须是包含一行或多行Python代码
的字符串,这些代码
将在受限制的环境中执行。
RExec.s_execfile(code)
在受限制的环境中执行包含在文件filename
中的Python代码。
RExec
对象还必须支持各种方法,这些方法将在受限环境中执行的代码隐式调用。覆盖子类中的这些方法用于更改受限环境强制的策略。
RExec.r_import(modulename[, globals[, locals[, fromlist]]])
导入模块模块名称
,ImportError
如果模块被认为是不安全的,则引发异常。
RExec.r_open(filename[, mode[, bufsize]])
open()
在受限制的环境中调用when的方法。参数与那些参数相同open()
,并且应该返回文件对象(或与文件对象兼容的类实例)。RExec
默认行为是允许打开任何文件进行读取,但禁止任何尝试写入文件。请参阅下面的示例,了解更少限制的实现r_open()
。
RExec.r_reload(module)
重新加载模块
对象模块
,重新解析并重新初始化它。
RExec.r_unload(module)
卸载模块
对象模块
(将其从受限制环境的sys.modules
字典中移除)。
和它们的等价物可以访问受限制的标准I / O流:
RExec.s_import(modulename[, globals[, locals[, fromlist]]])
导入模块模块名称
,ImportError
如果模块被认为是不安全的,则引发异常。
RExec.s_reload(module)
重新加载模块
对象模块
,重新解析并重新初始化它。
RExec.s_unload(module)
卸载模块
对象模块
。
2.定义受限制的环境
所述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服务器的目录中。
3.一个例子
让我们说,我们想要一个比标准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'):
# check filename: must begin with /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
,这将需要拆分文件名并对其执行各种操作。在安全受到威胁的情况下,编写简单的代码有时候会更加严格,而不是更复杂的代码,而且可能存在一个微妙的安全漏洞。