Heim  >  Artikel  >  Backend-Entwicklung  >  Python-Sicherheit: Schwachstellenanalyse und Lösungen im neuen String-Format

Python-Sicherheit: Schwachstellenanalyse und Lösungen im neuen String-Format

PHPz
PHPzOriginal
2017-05-01 09:53:521400Durchsuche

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.

Kernproblem

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.

Was ist das Problem?

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.

Gefahrenstufe

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]}

Sandbox-Formatierung

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__

Zusammenfassung:

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!

Stellungnahme:
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