Verwendung von Pythons eval() vs. ast.literal_eval()

Lesezeit: 7 Minuten

Benutzer-Avatar
tíko

Ich habe eine Situation mit einem Code, wo eval() kam als mögliche Lösung auf. Jetzt habe ich noch nie zu verwenden eval() vorher, aber ich bin auf viele Informationen über die potenzielle Gefahr gestoßen, die es verursachen kann. Das heißt, ich bin sehr vorsichtig mit der Verwendung.

Meine Situation ist, dass ich Eingaben von einem Benutzer habe:

datamap = input('Provide some data here: ')

Wo datamap muss ein Lexikon sein. Ich habe mich umgesehen und das gefunden eval() könnte das hinbekommen. Ich dachte, ich könnte die Art der Eingabe überprüfen, bevor ich versuche, die Daten zu verwenden, und das wäre eine praktikable Sicherheitsvorkehrung.

datamap = eval(input('Provide some data here: ')
if not isinstance(datamap, dict):
    return

Ich habe die Dokumente gelesen und bin mir immer noch nicht sicher, ob dies sicher wäre oder nicht. Bewertet eval die Daten direkt nach der Eingabe oder nach der datamap Variable heißt?

Ist der ast Moduls .literal_eval() die einzig sichere möglichkeit?

Benutzer-Avatar
Volatilität

datamap = eval(input('Provide some data here: ')) bedeutet, dass Sie den Code tatsächlich auswerten Vor Sie halten es für unsicher oder nicht. Es wertet den Code aus, sobald die Funktion aufgerufen wird. Siehe auch die Gefahren von eval.

ast.literal_eval löst eine Ausnahme aus, wenn die Eingabe kein gültiger Python-Datentyp ist, sodass der Code nicht ausgeführt wird, wenn dies nicht der Fall ist.

Verwenden ast.literal_eval wann immer du brauchst eval. Sie sollten normalerweise keine wörtlichen Python-Anweisungen auswerten.

  • Dies ist kein 100% richtiger Rat, da alle bitweisen Operatoren (oder überladenen Operatoren) fehlschlagen. Z.B. ast.literal_eval("1 & 1") wird einen Fehler werfen, aber eval("1 & 1") wird nicht.

    – Daniel van Flymen

    22. Mai 2017 um 0:34 Uhr

  • Nur neugierig. Sollten wir nicht Ausdrucksparser oder ähnliches verwenden, wenn wir so etwas wie “1 & 1” erwarten?

    – der Linuxer

    18. Januar 2018 um 0:52 Uhr

  • @thelinuxer sollten Sie immer noch, ja; Sie würden einfach nicht in der Lage sein, zu verwenden ast.literal_eval für so etwas (z. B. könnten Sie einen Parser manuell implementieren).

    – Volatilität

    18. Januar 2018 um 5:53 Uhr

  • @DanielvanFlymen – für mich zeigt dein Beispiel das ist guter Rat. Wenn Sie keine Operatoren (wie &) ausgewertet werden, verwenden Sie literal_eval. Die Tatsache, dass Sie dort keinen beliebigen Code zur Ausführung ablegen können, ist ein Feature, kein Fehler.

    – Ken Williams

    10. November 2021 um 22:06 Uhr

Benutzer-Avatar
Mixer

ast.literal_eval() betrachtet nur einen kleinen Teil der Python-Syntax als gültig:

Der bereitgestellte String oder Knoten darf nur aus den folgenden Python-Literalstrukturen bestehen: Strings, Bytes, Zahlen, Tupel, Listen, Diktate, Mengen, Boolesche Werte und None.

Vorbeigehen __import__('os').system('rm -rf /a-path-you-really-care-about') hinein ast.literal_eval() wird einen Fehler auslösen, aber eval() löscht gerne Ihre Dateien.

Da es so aussieht, als würden Sie den Benutzer nur ein einfaches Wörterbuch eingeben lassen, verwenden Sie ast.literal_eval(). Es tut sicher, was Sie wollen und nicht mehr.

Benutzer-Avatar
Kiran Kumar Kotari

bewerten:
Dies ist sehr leistungsfähig, aber auch sehr gefährlich, wenn Sie Strings zur Auswertung von nicht vertrauenswürdigen Eingaben akzeptieren. Angenommen, die ausgewertete Zeichenfolge ist “os.system(‘rm -rf /’)” ? Es fängt wirklich an, alle Dateien auf Ihrem Computer zu löschen.

ast.literal_eval:
Evaluieren Sie sicher einen Ausdrucksknoten oder eine Zeichenfolge, die ein Python-Literal oder eine Containeranzeige enthält. Der bereitgestellte String oder Knoten darf nur aus den folgenden Python-Literalstrukturen bestehen: Strings, Bytes, Zahlen, Tupel, Listen, Diktate, Sets, Booleans, None, Bytes und Sets.

Syntax:

eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)

Beispiel:

# python 2.x - doesn't accept operators in string format
import ast
ast.literal_eval('[1, 2, 3]')  # output: [1, 2, 3]
ast.literal_eval('1+1') # output: ValueError: malformed string


# python 3.0 -3.6
import ast
ast.literal_eval("1+1") # output : 2
ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'}
# type dictionary
ast.literal_eval("",{}) # output : Syntax Error required only one parameter
ast.literal_eval("__import__('os').system('rm -rf /')") # output : error

eval("__import__('os').system('rm -rf /')") 
# output : start deleting all the files on your computer.
# restricting using global and local variables
eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{})
# output : Error due to blocked imports by passing  '__builtins__':{} in global

# But still eval is not safe. we can access and break the code as given below
s = """
(lambda fc=(
lambda n: [
    c for c in 
        ().__class__.__bases__[0].__subclasses__() 
        if c.__name__ == n
    ][0]
):
fc("function")(
    fc("code")(
        0,0,0,0,"KABOOM",(),(),(),"","",0,""
    ),{}
)()
)()
"""
eval(s, {'__builtins__':{}})

Im obigen Code ().__class__.__bases__[0] nichts als Objekt selbst. Jetzt haben wir alle instanziiert Unterklassenhier unsere wichtigsten enter code hereZiel ist es, eine benannte Klasse zu finden n davon.

Wir müssen code Objekt und function Objekt aus instanziierten Unterklassen. Dies ist ein alternativer Weg von CPython um auf Unterklassen von Objekten zuzugreifen und das System anzuhängen.

Ab Python 3.7 ist ast.literal_eval() jetzt strenger. Addition und Subtraktion beliebiger Zahlen sind nicht mehr erlaubt. Verknüpfung

  • Ich verwende Python 2.7 und habe gerade überprüft, ob es unter Python 3.x einwandfrei funktioniert. Mein Fehler, ich habe es immer wieder auf Python 2.7 versucht

    – Murya

    23. September 2016 um 7:18 Uhr

  • ast.literal_eval("1+1") funktioniert nicht in Python 3.7 und wie bereits erwähnt, sollte literal_eval auf Literale dieser wenigen Datenstrukturen beschränkt sein. Es sollte nicht in der Lage sein, eine binäre Operation zu analysieren.

    – Sesshu

    3. Oktober 2018 um 12:52 Uhr


  • Könnten Sie Ihre erklären KABOOM Code, bitte? Hier gefunden: KABOOM

    – winklerrr

    4. Januar 2019 um 8:57 Uhr


  • @winklerrr KABOOM ist hier schön erklärt: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html

    – Elijas Dapšauskas

    18. Februar 2019 um 15:32 Uhr


In neuerer Version von Python3 analysiert ast.literal_eval() keine einfachen Strings mehr, stattdessen sollten Sie die Methode ast.parse() verwenden, um einen AST zu erstellen und ihn dann zu interpretieren.

Dies ist ein vollständiges Beispiel für die korrekte Verwendung von ast.parse() in Python 3.6+, um einfache arithmetische Ausdrücke sicher auszuwerten.

import ast, operator, math
import logging

logger = logging.getLogger(__file__)

def safe_eval(s):

    def checkmath(x, *args):
        if x not in [x for x in dir(math) if not "__" in x]:
            raise SyntaxError(f"Unknown func {x}()")
        fun = getattr(math, x)
        return fun(*args)

    binOps = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.truediv,
        ast.Mod: operator.mod,
        ast.Pow: operator.pow,
        ast.Call: checkmath,
        ast.BinOp: ast.BinOp,
    }

    unOps = {
        ast.USub: operator.neg,
        ast.UAdd: operator.pos,
        ast.UnaryOp: ast.UnaryOp,
    }

    ops = tuple(binOps) + tuple(unOps)

    tree = ast.parse(s, mode="eval")

    def _eval(node):
        if isinstance(node, ast.Expression):
            logger.debug("Expr")
            return _eval(node.body)
        elif isinstance(node, ast.Str):
            logger.debug("Str")
            return node.s
        elif isinstance(node, ast.Num):
            logger.debug("Num")
            return node.value
        elif isinstance(node, ast.Constant):
            logger.info("Const")
            return node.value
        elif isinstance(node, ast.BinOp):
            logger.debug("BinOp")
            if isinstance(node.left, ops):
                left = _eval(node.left)
            else:
                left = node.left.value
            if isinstance(node.right, ops):
                right = _eval(node.right)
            else:
                right = node.right.value
            return binOps[type(node.op)](left, right)
        elif isinstance(node, ast.UnaryOp):
            logger.debug("UpOp")
            if isinstance(node.operand, ops):
                operand = _eval(node.operand)
            else:
                operand = node.operand.value
            return unOps[type(node.op)](operand)
        elif isinstance(node, ast.Call):
            args = [_eval(x) for x in node.args]
            r = checkmath(node.func.id, *args)
            return r
        else:
            raise SyntaxError(f"Bad syntax, {type(node)}")

    return _eval(tree)


if __name__ == "__main__":
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    logger.addHandler(ch)
    assert safe_eval("1+1") == 2
    assert safe_eval("1+-5") == -4
    assert safe_eval("-1") == -1
    assert safe_eval("-+1") == -1
    assert safe_eval("(100*10)+6") == 1006
    assert safe_eval("100*(10+6)") == 1600
    assert safe_eval("2**4") == 2**4
    assert safe_eval("sqrt(16)+1") == math.sqrt(16) + 1
    assert safe_eval("1.2345 * 10") == 1.2345 * 10

    print("Tests pass")

Wenn Sie nur ein vom Benutzer bereitgestelltes Wörterbuch benötigen, ist dies eine mögliche bessere Lösung json.loads. Die Hauptbeschränkung besteht darin, dass json dicts Zeichenfolgenschlüssel erfordert. Außerdem können Sie nur wörtliche Daten angeben, aber das gilt auch für literal_eval.

Benutzer-Avatar
Haziq

Ich war hängengeblieben ast.literal_eval(). Ich habe es im IntelliJ IDEA Debugger versucht und es kam immer wieder zurück None auf Debugger-Ausgabe.

Aber später, als ich seine Ausgabe einer Variablen zuwies und sie im Code druckte. Es funktionierte gut. Codebeispiel teilen:

import ast
sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]'
output_value = ast.literal_eval(sample_string)
print(output_value)

Seine Python-Version 3.6.

Benutzer-Avatar
João Raffs

statt zu importieren asthabe ich gerade zugeschrieben eval(data.decode('utf-8)) in die Dictionary-Variable, wie folgt:

dict = eval(data.decode('utf-8'))

klappt wunderbar!

  • Ich kann nicht sagen, ob das ein Witz ist, also gehe ich davon aus, dass es keiner ist. Die Antwort ist wegen gefährlich evales ist schlechte Python, weil es das eingebaute beschattet dict und es beantwortet nicht wirklich die Frage von OP.

    – jDo

    11. Juli um 14:10 Uhr

1062420cookie-checkVerwendung von Pythons eval() vs. ast.literal_eval()

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy