<p>这就是我的解决方案。有关此方法的安全性的讨论,请参见</p>
<ul>
<li><a href="https://stackoverflow.com/questions/10661079/restricting-pythons-syntax-to-execute-user-code-safely-is-this-a-safe-approach">Restricting Python's syntax to execute user code safely. Is this a safe approach?</a></li>
</ul>
<p>多亏了<a href="https://stackoverflow.com/users/499476/arifwn">arifwn</a>,我开始研究Python的<code>ast</code>(抽象语法树)模块。此模块提供一个用于遍历树的<code>ast.NodeVisitor</code>。这段代码将<code>NodeVisitor</code>子类化,以创建一个语法检查器,该检查器将基本数学所需的代码白名单。函数调用和名称是特别监视的,因为只允许某些函数,只允许使用未使用的名称。在</p>
<pre><code>import ast
allowed_functions = set([
#math library
'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh',
'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf',
'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod',
'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp',
'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians',
'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc',
#builtins
'abs', 'max', 'min', 'range', 'xrange'
])
allowed_node_types = set([
#Meta
'Module', 'Assign', 'Expr',
#Control
'For', 'If', 'Else',
#Data
'Store', 'Load', 'AugAssign', 'Subscript',
#Datatypes
'Num', 'Tuple', 'List',
#Operations
'BinOp', 'Add', 'Sub', 'Mult', 'Div', 'Mod', 'Compare'
])
safe_names = set([
'True', 'False', 'None'
])
class SyntaxChecker(ast.NodeVisitor):
def check(self, syntax):
tree = ast.parse(syntax)
self.passed=True
self.visit(tree)
def visit_Call(self, node):
if node.func.id not in allowed_functions:
raise SyntaxError("%s is not an allowed function!"%node.func.id)
else:
ast.NodeVisitor.generic_visit(self, node)
def visit_Name(self, node):
try:
eval(node.id)
except NameError:
ast.NodeVisitor.generic_visit(self, node)
else:
if node.id not in safe_names and node.id not in allowed_functions:
raise SyntaxError("%s is a reserved name!"%node.id)
else:
ast.NodeVisitor.generic_visit(self, node)
def generic_visit(self, node):
if type(node).__name__ not in allowed_node_types:
raise SyntaxError("%s is not allowed!"%type(node).__name__)
else:
ast.NodeVisitor.generic_visit(self, node)
if __name__ == '__main__':
x = SyntaxChecker()
while True:
try:
x.check(raw_input())
except Exception as e:
print e
</code></pre>
<p>请注意,这是为了只接受代码的数学部分而设计的,提供了函数定义和返回语句。在</p>
<p>这种将所有必需的安全构造(尤其是白名单所需的不安全构造)白名单的方法可以修改为生成许多有用的Python子集;非常适合用户脚本!在</p>
<p>注意,为了安全地执行,它应该在它自己的线程中有一个超时,以便在用户代码生成无限循环或类似循环时减少名称冲突和超时。在</p>