Kürzlich ist mir eine Sicherheitslücke bei der Formatierung von Zeichenfolgen aufgefallen. Ich habe eine ausführliche Analyse durchgeführt und entsprechende Sicherheitsmaßnahmen bereitgestellt.
Wenn wir str.format für nicht vertrauenswürdige Benutzereingaben verwenden, birgt dies Sicherheitsrisiken – ich kenne dieses Problem tatsächlich schon seit langem, habe aber bis heute nicht wirklich erkannt, wie schwerwiegend es ist. Da Angreifer damit die Jinja2-Sandbox umgehen können, führt dies zu ernsthaften Problemen mit Informationslecks. In der Zwischenzeit stelle ich am Ende dieses Artikels eine neue sichere Version von str.format bereit.
Es sollte daran erinnert werden, dass dies ein ziemlich ernstes Sicherheitsrisiko darstellt. Der Grund, warum ich hier einen Artikel schreibe, ist, dass die meisten Menschen wahrscheinlich nicht wissen, wie einfach es ist, es auszunutzen.
Ab Python 2.6 hat Python eine neue Syntax zum Formatieren von Zeichenfolgen eingeführt, die von .NET inspiriert ist. Natürlich unterstützen neben Python auch Rust und einige andere Programmiersprachen diese Syntax. Mit Hilfe der .format()-Methode kann diese Syntax sowohl auf Byte- als auch auf Unicode-Strings angewendet werden (in Python 3 nur auf Unicode-Strings) und kann auch auf anpassbarere Strings abgebildet werden.
Ein Merkmal dieser Syntax besteht darin, dass sie es ermöglicht, die Positions- und Schlüsselwortargumente des String-Formats zu bestimmen und die Datenelemente jederzeit explizit neu anzuordnen. Darüber hinaus kann es sogar auf die Eigenschaften und Datenelemente des Objekts zugreifen – was hier die Hauptursache für das Sicherheitsproblem darstellt.
Insgesamt kann man dies ausnutzen, um Folgendes zu tun:
>>> 'class of {0} is {0.__class__}'.format(42) "class of 42 is "
Im Wesentlichen hat jeder, der die Kontrolle über die Formatzeichenfolge hat, die Möglichkeit, auf verschiedene interne Eigenschaften des Objekts zuzugreifen.
Die erste Frage ist, wie man die Formatzeichenfolge steuert. Sie können an folgenden Stellen beginnen:
1. Nicht vertrauenswürdiger Übersetzer in der String-Datei. Wir werden wahrscheinlich damit durchkommen, da viele in mehrere Sprachen übersetzte Anwendungen diese neue Python-String-Formatierungsmethode verwenden, aber nicht jeder wird eine gründliche Überprüfung aller eingegebenen Strings durchführen.
2. Vom Benutzer bereitgestellte Konfiguration. Da einige Systembenutzer bestimmte Verhaltensweisen konfigurieren können, werden diese Konfigurationen möglicherweise in Form von Formatzeichenfolgen angezeigt. Besonders hervorzuheben ist, dass einige Benutzer Benachrichtigungs-E-Mails, Protokollnachrichtenformate oder andere grundlegende Vorlagen über die Webanwendung konfiguriert haben.
Wenn Sie nur das C-Interpreterobjekt an die Formatzeichenfolge übergeben, besteht keine große Gefahr, da Sie in diesem Fall höchstens einige Ganzzahlklassen offenlegen.
Sobald jedoch ein Python-Objekt an diese Formatzeichenfolge übergeben wird, wird es problematisch. Das liegt daran, dass die Menge an Dingen, die von Python-Funktionen bereitgestellt werden können, ziemlich atemberaubend ist. Hier ist ein Szenario für eine hypothetische Webanwendung, die den Schlüssel preisgeben könnte:
CONFIG = { 'SECRET_KEY': 'super secret key' } class Event(object): def __init__(self, id, level, message): self.id = id self.level = level self.message = message def format_event(format_string, event): return format_string.format(event=event)
Wenn der Benutzer hier format_string einfügen könnte, würde er so etwas wie diese geheime Zeichenfolge finden:
{event.__init__.__globals__[CONFIG][SECRET_KEY]}
Was ist, wenn Sie jemand anderen benötigen, der die Formatierungszeichenfolge bereitstellt? Tatsächlich können einige undokumentierte interne Mechanismen verwendet werden, um das Formatierungsverhalten von Zeichenfolgen zu ändern.
from string import Formatter from collections import Mapping class MagicFormatMapping(Mapping): """This class implements a dummy wrapper to fix a bug in the Python standard library for string formatting. See http://bugs.python.org/issue13598 for information about why this is necessary. """ def __init__(self, args, kwargs): self._args = args self._kwargs = kwargs self._last_index = 0 def __getitem__(self, key): if key == '': idx = self._last_index self._last_index += 1 try: return self._args[idx] except LookupError: pass key = str(idx) return self._kwargs[key] def __iter__(self): return iter(self._kwargs) def __len__(self): return len(self._kwargs) # This is a necessary API but it's undocumented and moved around # between Python releases try: from _string import formatter_field_name_split except ImportError: formatter_field_name_split = lambda \ x: x._formatter_field_name_split() {C} class SafeFormatter(Formatter): def get_field(self, field_name, args, kwargs): first, rest = formatter_field_name_split(field_name) obj = self.get_value(first, args, kwargs) for is_attr, i in rest: if is_attr: obj = safe_getattr(obj, i) else: obj = obj[i] return obj, first def safe_getattr(obj, attr): # Expand the logic here. For instance on 2.x you will also need # to disallow func_globals, on 3.x you will also need to hide # things like cr_frame and others. So ideally have a list of # objects that are entirely unsafe to access. if attr[:1] == '_': raise AttributeError(attr) return getattr(obj, attr) def safe_format(_string, *args, **kwargs): formatter = SafeFormatter() kwargs = MagicFormatMapping(args, kwargs) return formatter.vformat(_string, args, kwargs)
Jetzt können wir die Methode „safe_format“ verwenden, um str.format zu ersetzen:
>>> '{0.__class__}'.format(42) "" >>> safe_format('{0.__class__}', 42) Traceback (most recent call last): File "", line 1, in AttributeError: __class__
In der Programmentwicklung gibt es ein Sprichwort: „any“. Vertrauen Sie niemals Benutzereingaben! Nun scheint es, dass dieser Satz vollkommen Sinn ergibt. Also Studierende, bitte behaltet dies im Hinterkopf!
【Kursempfehlung】
Python Kostenloses Online-Video-Tutorial
Das obige ist der detaillierte Inhalt vonPython-Sicherheit: Schwachstellenanalyse und Lösungen im neuen String-Format. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!