Verwenden Sie pythons eval () vs. ast.literal_eval ()?

176

Ich habe eine Situation mit Code, in der eval()eine mögliche Lösung gefunden wurde. Jetzt musste ich noch nie etwas verwenden, eval()aber ich habe viele Informationen über die potenzielle Gefahr gefunden, die es verursachen kann. Trotzdem bin ich sehr vorsichtig, wenn ich es benutze.

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

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

Wo datamapmuss ein Wörterbuch sein. Ich suchte herum und fand heraus, dass eval()das klappen könnte. Ich dachte, ich könnte möglicherweise die Art der Eingabe überprüfen, bevor ich versuche, die Daten zu verwenden, und das wäre eine praktikable Sicherheitsmaßnahme.

datamap = eval(raw_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 ist oder nicht. Wertet eval die Daten aus, sobald sie eingegeben wurden oder nachdem die datamapVariable aufgerufen wurde?

Ist das astModul .literal_eval()die einzig sichere Option?

Tijko
quelle

Antworten:

189

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

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

Verwenden ast.literal_evalSie, wann immer Sie brauchen eval. Sie sollten normalerweise keine wörtlichen Python-Anweisungen auswerten.

Volatilität
quelle
20
Dies ist kein 100% korrekter Rat, da bitweise Operatoren (oder überladene Operatoren) fehlschlagen. Z.B. ast.literal_eval("1 & 1")wird einen Fehler auslösen, aber eval("1 & 1")nicht.
Daniel van Flymen
1
Nur neugierig. Sollten wir keine Ausdrucksparser oder ähnliches verwenden, wenn wir etwas wie "1 & 1" erwarten?
Thelinuxer
@thelinuxer solltest du noch, ja; Sie ast.literal_evalkönnten einfach nicht für so etwas verwenden (z. B. könnten Sie einen Parser manuell implementieren).
Volatilität
104

ast.literal_eval() betrachtet nur eine kleine Teilmenge der Python-Syntax als gültig:

Die angegebene Zeichenfolge oder der angegebene Knoten besteht möglicherweise nur aus den folgenden Python-Literalstrukturen: Zeichenfolgen, Zahlen, Tupel, Listen, Diktate, Boolesche Werte und Keine.

Wenn Sie __import__('os').system('rm -rf /a-path-you-really-care-about')in übergeben, ast.literal_eval()wird ein Fehler eval()ausgelöst, Ihr Laufwerk wird jedoch gerne gelöscht.

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

Mixer
quelle
unterstützt auch Byte-Strings (Klassenbytes). Z.B. b'Hello World '
XChikuX
52

eval: Dies ist sehr mächtig, aber auch sehr gefährlich, wenn Sie Zeichenfolgen akzeptieren, die aus nicht vertrauenswürdigen Eingaben ausgewertet werden sollen. Angenommen, die zu bewertende Zeichenfolge lautet "os.system ('rm -rf /')"? Es werden wirklich alle Dateien auf Ihrem Computer gelöscht.

ast.literal_eval: Bewerten Sie sicher einen Ausdrucksknoten oder eine Zeichenfolge, die ein Python-Literal oder eine Containeranzeige enthält. Die bereitgestellte Zeichenfolge oder der bereitgestellte Knoten darf nur aus den folgenden Python-Literalstrukturen bestehen: Zeichenfolgen, Bytes, Zahlen, Tupel, Listen, Diktate, Mengen, Boolesche Werte, Keine, Bytes und Mengen.

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 Unterklassen instanziiert . Hier besteht unser enter code hereHauptziel darin, eine Klasse mit dem Namen n daraus zu finden.

Wir müssen Einwände erheben codeund functionEinwände gegen instanziierte Unterklassen erheben. Dies ist eine alternative Möglichkeit, CPythonum auf Unterklassen von Objekten zuzugreifen und das System anzuhängen.

Ab Python 3.7 ist ast.literal_eval () jetzt strenger. Das Addieren und Subtrahieren beliebiger Zahlen ist nicht mehr zulässig. Verknüpfung

Kiran Kumar Kotari
quelle
1
Ich benutze Python 2.7 und ich habe gerade überprüft, ob es auf Python 3.x gut funktioniert. Mein schlechtes ich versuchte es auf Python 2.7
Mourya
3
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
Könnten Sie KABOOMbitte Ihren Code erklären ? Fand es hier:KABOOM
winklerrr
2
@winklerrr KABOOMwird hier schön erklärt: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Elijas
41

Python ist eifrig bei der Auswertung und eval(raw_input(...))bewertet daher die Benutzereingaben, sobald sie auf die treffen eval, unabhängig davon, was Sie anschließend mit den Daten tun. Daher ist dies nicht sicher , insbesondere wenn Sie evalBenutzereingaben vornehmen.

Verwenden Sie ast.literal_eval.


Wenn Sie dies beispielsweise an der Eingabeaufforderung eingeben, ist dies für Sie sehr, sehr schlecht:

__import__('os').system('rm -rf /a-path-you-really-care-about')
nneonneo
quelle
3

Wenn Sie nur ein vom Benutzer bereitgestelltes Wörterbuch benötigen, ist eine bessere Lösung möglich json.loads. Die Hauptbeschränkung besteht darin, dass für json dicts Zeichenfolgenschlüssel erforderlich sind. Sie können auch nur Literaldaten angeben, dies gilt jedoch auch für literal_eval.

Chinasaurier
quelle
1

Ich war fest mit ast.literal_eval(). Ich habe es im IntelliJ IDEA-Debugger versucht und es wurde immer wieder Nonebei der Debugger-Ausgabe zurückgegeben.

Aber später, als ich seine Ausgabe einer Variablen zuordnete und sie in Code druckte. Es hat gut funktioniert. Beispiel für das Teilen von Code:

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.

M Haziq
quelle