Python安全沙箱RestrictedPython 设计理念
Python 是一种图灵完备的编程语言。在 Web 上下文中为用户提供 Python 接口是一种潜在的安全风险。Web 框架和内容管理系统 (CMS) 希望通过 Web (TTW) 为其用户提供尽可能多的可扩展性,这也意味着用户有权通过 Python 脚本添加功能。
抛开 RestrictedPython 不谈,根据信息安全最佳实践,应该采取额外的预防措施来确保应用程序和服务器本身的完整性。
RestrictedPython 定义了 Python 编程语言的一个安全子集,这是增强语言安全性的一种常用做法,比如Ada的Ravenscar profile 就是这种做法的另一个例子。
定义语言的安全子集涉及限制EBNF元素,还有要显示允许或禁止某些语言特性。编程语言的大部分功能来自其标准库和其它库,因此任何对这些方法的调用都必须检查并可能需要进行限制。RestrictedPython 通常不允许调用任何未明确列入白名单的库。
由于 Python 是一种由解释器执行的脚本语言,因此在解释器执行生成的字节码之前,任何要执行的 Python 代码都必须进行显示地检查。
Python 提供了完成该流程的三种方法:
-
compile()
将源代码编译为字节码 -
exec
/exec()
在解释器中执行字节码 -
eval
/eval()
执行字节码表达式
因此 RestrictedPython 提供了 python 内置的compile()
函数(Python 2 / Python 3)的替代函数,该Python内置函数定义如下:
compile(source, filename, mode [, flags [, dont_inherit]])
该compile()
函数的定义随着版本不同而不同,但其相关参数source
并mode
一直存在。
有三个有效的字符串值mode
:
-
'exec'
-
'eval'
-
'single'
对于 RestrictedPython,此compile()
函数被替换为:
RestrictedPython.compile_restricted(source, filename, mode [, flags [, dont_inherit]])
主要参数source
必须是字符串或是ast.AST
的实例。这两个方法都会返回已编译好的解释器可以执行的字节码,如果源代码无效则会抛出异常。
由于compile
与compile_restricted
仅仅是将源码编译为字节码,因此代码仍然可以调用库文件,所以这作为沙箱来说仍然是不够的。
注意到,下面的两个方法/语句:
-
exec
/exec()
-
eval
/eval()
有两个参数:
-
globals
-
locals
它们是对 Python 内置函数的引用。
因此通过修改和限制globals
,locals
中可用的模块、方法和常量,我们就可以限制可能的调用。
此外,RestrictedPython 还提供了定义属性保护策略的能力,该策略允许开发人员保护对属性的访问。这通过定义以下函数的限制版本来实现:
-
print
-
getattr
-
setattr
-
import
RestrictedPython 还提供了 Python 的三个预定义的受限__builtins__
:
-
safe_builtins
(在 Guards.py 中提供) -
limited_builtins
(在 Limits.py 中提供),它提供了受限的序列类型 -
utilities_builtins
(在 Utilities.py 中提供),它提供对标准模块数学、随机、字符串和集合的访问。
-
safe_globals
(在 Guards.py 中提供)相当于{'__builtins__': safe_builtins}
此外,还有一些保护函数可以使 Python 对象的属性不可变: full_write_guard