众所周知在Python下可以用eval和exec来执行任意的python表达式及脚本,但是,当运行的脚本来自于网络时这样做就会有安全风险,你总不想运行包含"import os; os.system('rm -rf /')"的脚本吧?对这类问题,通常的解决方法是为来自网络的脚本创建一个沙箱(Sandbox),浏览器在执行来自网络的Java Applet时会创建一个权限较低的Sandbox,在其中运行的代码不能做任何威胁系统的操作,例如读取/写入本地文件,执行外部程序等。Python运行环境并不原生支持创建Sandbox,它不像java在执行危险操作会先进行权限检查,而[url=http://pypi.python.org/pypi/RestrictedPython]RestrictedPython[/url]提供了一种可选的创建sandbox的方式。
[b][size=medium]RestrictedPython原理[/size][/b]
RestrictedPython的关键在于使用compile_restricted去编译Python代码,它和内置compile差不多也是将源代码编译成exec或eval可以直接运行的AST(语法抽象树)。和compile不一样的地方在于,compile_restricted悄悄地改变地生成的AST,它将读/写属性、访问/更新字典变成可以hook的函数调用。例如,它将x.foo变成_getattr_(x, 'foo'),将x.foo = 'bar'变成_write_(x).foo = 'bar',将x['foo']变成_getitem_(x, 'foo'),x['foo'] = 'bar'变成_write_(x)['foo'] = 'bar'。通过提供你自己的_getattr_,_write_,_getitem_等函数,你可以控制运行脚本所能干的事情,即为脚本创建一个Sandbox。这种创建Sandobx方式有些类似于Java中的AspectJ,通过改变生成的java的字节码来增加类的行为。
[b][size=medium]1. 控制导入[/size][/b]
要控制导入只需要定制__import__函数,这是python的机制,和RestrictedPython无关,如果你不需要复杂的控制,通过定制__import__,控制__builtins__导入的变量,并在globals中导入一些需要的变量或模块来实现。要实现不能导入os模块的效果,可以这样做:
[b][size=medium]RestrictedPython原理[/size][/b]
RestrictedPython的关键在于使用compile_restricted去编译Python代码,它和内置compile差不多也是将源代码编译成exec或eval可以直接运行的AST(语法抽象树)。和compile不一样的地方在于,compile_restricted悄悄地改变地生成的AST,它将读/写属性、访问/更新字典变成可以hook的函数调用。例如,它将x.foo变成_getattr_(x, 'foo'),将x.foo = 'bar'变成_write_(x).foo = 'bar',将x['foo']变成_getitem_(x, 'foo'),x['foo'] = 'bar'变成_write_(x)['foo'] = 'bar'。通过提供你自己的_getattr_,_write_,_getitem_等函数,你可以控制运行脚本所能干的事情,即为脚本创建一个Sandbox。这种创建Sandobx方式有些类似于Java中的AspectJ,通过改变生成的java的字节码来增加类的行为。
[b][size=medium]1. 控制导入[/size][/b]
要控制导入只需要定制__import__函数,这是python的机制,和RestrictedPython无关,如果你不需要复杂的控制,通过定制__import__,控制__builtins__导入的变量,并在globals中导入一些需要的变量或模块来实现。要实现不能导入os模块的效果,可以这样做:
src = '''
import os
'''