Verwendung von Pythons eval() vs. ast.literal_eval()
Lesezeit: 7 Minuten
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?
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
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.
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.
# 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
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.
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:
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
10624200cookie-checkVerwendung von Pythons eval() vs. ast.literal_eval()yes