Используя ast и белые списки, чтобы сделать eval () python безопасным?
ХОРОШО. язнать эксперты иметь разговорный и ты не долженever использовать Pythoneval()
на ненадежных данных, когда-либо. Я не умнее, чем весь остальной мир, и даже не должен пробовать это. Но! Я все равно собираюсь.
Моя основная проблема заключается в том, что я собираюсь написать небольшую программу для вычислений калькулятора, которая будет принимать ненадежные входные данные, используя подмножество синтаксиса python. Я знаю: использоватькурсировать или жеPyparsing и напиши парсер и поехали. Бродить с мимолетными глобалами и местными жителямиeval()
не будет делать трюк.
Все подходы, которые я видел (и о которых мы подозревали), пытаются перечислить зло. Здесь я пытаюсь перечислитьgood - получить AST, разрешить только несколько типов узлов, а затем убедиться, что любые вызовы относятся к одной из набора функций из белого списка. Вот мини-реализация (исуть):
import ast
import math
SAFE_FX = {
'exp': math.exp,
}
SAFE_NODES = set(
(ast.Expression,
ast.Num,
ast.Call,
ast.Name,
ast.Load,
ast.BinOp,
ast.Add,
ast.Sub,
ast.Mult,
ast.Div,)
)
class CleansingNodeVisitor(ast.NodeVisitor):
def generic_visit(self, node):
if type(node) not in SAFE_NODES:
raise Exception("%s not in SAFE_NODES" % type(node))
super(CleansingNodeVisitor, self).generic_visit(node)
def visit_Call(self, call):
if call.func.id not in SAFE_FX:
raise Exception("Unknown function: %s" % call.func.id)
def my_safe_eval(s):
tree = ast.parse(s, mode='eval')
cnv = CleansingNodeVisitor()
cnv.visit(tree)
compiled = compile(tree, s, "eval")
return(eval(compiled, SAFE_FX))
Так,my_safe_eval('2*(4+exp(1.3))')
работает, покаmy_safe_eval('[].__class__')
трюки иmy_safe_eval('open("/something/evil")')
также запрещено - без запрета__builtins__
или же__locals__
или что-нибудь.
Я ... я думаю, что это работает. Я сумасшедший?