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?

questionAnswers(2)

yourAnswerToTheQuestion