Używając ast i białych list, aby uczynić eval Pythona () bezpiecznym?
DOBRZE. jawiedzieć eksperci mieć Mówiony i nie powinieneśzawsze użyj Pythonaeval()
na niezaufanych danych. Nie jestem mądrzejszy od reszty świata i nawet nie powinienem tego próbować. Ale! W każdym razie idę.
Moim podstawowym problemem jest to, że chcę napisać mały program oceniający kalkulator, który pobierze niezaufane dane wejściowe, używając podzbioru składni pythona. Wiem: użyjzagięcie lubparsowanie i napisz parser, a my pójdziemy. Wkręcając się w przekazywanie globali i mieszkańców doeval()
nie załatwi sprawy.
Wszystkie podejścia, które widziałem (i byłem o nich bardzo ciekawy), próbują wyliczyć zło. Tutaj próbuję wyliczyćdobry - zdobądź AST, zezwól tylko na kilka typów węzłów, a następnie sprawdź, czy wszystkie połączenia należą do jednego z zestawów funkcji na białej liście. Oto mini-implementacja (iistota):
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))
Więc,my_safe_eval('2*(4+exp(1.3))')
działa, chociażmy_safe_eval('[].__class__')
sztuczki imy_safe_eval('open("/something/evil")')
jest również zabronione - bez zakazu__builtins__
lub__locals__
lub cokolwiek.
Ja ... myślę, że to działa. Czy jestem zły?