Skip to main content

So verhindern Sie XPath-Injection-Angriffe

Artikel von

Marcelo Oliveira

blog feature pypi spoof

10. Mai 2023

0 Min. Lesezeit

Webanwendungen sind für verschiedene Arten von Angriffen anfällig, besonders jedoch für Code-Injection-Angriffe. Ein solcher Angriff, die XPath-Injection, nutzt Websites aus, die von Nutzern bereitgestellte Informationen benötigen, um auf Daten im XML-Format zuzugreifen. Alle Websites, die eine Datenbank im XML-Format verwenden, könnten für diesen Angriff anfällig sein.

XPath ist eine Abfragesyntax, mit der Websites ihre XML-Datenspeicher durchsuchen können. Korrekt ausgeführte XPath-Abfragen sind ein legitimes Mittel, um Daten in Elementen und Attributen zu finden. Websites mit Datenbanken, die Daten im XML-Format enthalten, integrieren Nutzereingaben (z. B. einen Benutzernamen und ein Passwort) in XPath-Abfragen, um die benötigten Daten zu finden.

Wie bei anderen Injection-Angriffen (etwa dem bekannten SQL-Injection-Angriff) können Angreifer diesen Prozess jedoch mit fehlerhaften Informationen ausnutzen. Ein Angreifer kann herausfinden, wie die XML-Daten strukturiert sind, und auf Daten zugreifen, für die er normalerweise keine Berechtigung hätte. Angreifer, die diese Informationen erhalten, können ihre Zugriffsrechte erweitern (ein Angriff zur „Rechteausweitung“), möglicherweise die Anwendung und vertrauliche Daten kompromittieren und die Informationen oder den Zugriff nutzen, um den Rest des Unternehmens anzugreifen.

Neben dem Löschen oder Beschädigen von Daten kann Datenexfiltration Hackern ermöglichen, Anwendungsbefehle auszuführen, weitere Server-Angriffe vorzubereiten und das gesamte Unternehmen mit anderen Angriffen lahmzulegen. Die Folgen solcher Angriffe können dem Ruf unserer Anwendungen und der Glaubwürdigkeit eines Unternehmens bei seinen Kunden schaden oder diese zerstören.

Zum Glück können wir unsere Websites vor XPath-Injection-Angriffen schützen. In diesem Artikel untersuchen wir XML-Schwachstellen und erfahren, wie Sie verhindern, dass sie Anwendungsdaten gefährden.

XPath-Injection-Schwachstellen erkennen und beheben

Um zu erfahren, wie wir XPath-Injections verhindern können, sehen wir uns zunächst ein Beispiel an. Anschließend erfahren wir, wie wir unsere Apps und Daten am besten vor diesem Angriff schützen.

Eine anfällige Authentifizierungs-App untersuchen

Sehen wir uns eine Anwendung an, bei der sich Nutzer mit einem Benutzernamen und einem Passwort anmelden müssen.

Erstellen Sie einen Ordner namens xpath und darin eine Datei namens users_info.xml mit den folgenden XML-Daten:

<users>

  <userinfo>
    <user_id>1</user_id>
    <username>jennifer</username>
    <password>4uyFh6v0</password>
    <account_no>757456</account_no>
  </userinfo>

  <userinfo>
    <user_id>2</user_id>
    <username>kimkimani</username>
    <password>6wreD1678</password>
    <account_no>870965</account_no>
  </userinfo>

  <userinfo>
    <user_id>3</user_id>
    <username>michael</username>
    <password>gf5dG63g</password>
    <account_no>345689</account_no>
  </userinfo>

  <userinfo>
    <user_id>4</user_id>
    <username>robert</username>
    <password>yH8wej3h</password>
    <account_no>096417</account_no>
  </userinfo>

</users>

Erstellen Sie im Ordner 0xpath eine neue Datei namens main.py und fügen Sie den folgenden Code ein:

# Import the required modules
from lxml import etree

# Parse the XML data
tree = etree.parse("users_info.xml")

def login(username, password):
	expression = "/users/userinfo[username='" + username + "' and password='" + password + "']"

	results = tree.xpath(expression)

	# if no results, which means login details did not match
	if len(results) < 1:
    		print("Incorrect credentials! Check your username or password.")

	# if login details match
	else:
    	for result in results:
        	print(f'Login successful! username: {result[1].text}, password: {result[2].text}') 
# Get the user-provided input
username = input("Enter your username: ")
password = input("Enter your password: ")

login(username, password)

Der Code lädt die XML-Daten und führt anschließend eine XPath-Abfrage aus, um die XML-Daten auszuwählen, die mit den vom Nutzer bereitgestellten Anmeldedaten übereinstimmen. Wird eine Übereinstimmung gefunden, authentifiziert die App den Nutzer. Andernfalls zeigt die App die Meldung „Ungültige Anmeldedaten! Überprüfen Sie Ihren Benutzernamen oder Ihr Passwort.“ an.

Installieren Sie vor dem Testen dieses Codes das Python-Paket lxml, um XML-Daten zu verarbeiten. Führen Sie zum Installieren diesen Befehl aus:

pip install lxml

Um den Code zu testen, rufen Sie die Funktion login auf und übergeben Sie Ihren Benutzernamen und Ihr Passwort. Fügen Sie dazu diesen Codeausschnitt am Ende Ihrer Datei main.py ein:

login(username="kimkimani", password="6wreD1678")

Führen Sie den folgenden Befehl im Terminal aus, um die Datei auszuführen.

python3 main.py

Eine Erfolgsmeldung wird angezeigt. Sie können sich auch mit falschen Anmeldedaten anmelden. In diesem Fall wird eine Fehlermeldung angezeigt.

Dieser Code ist anfällig für XPath-Injection-Angriffe, weil er die Nutzereingabe mit der XPath-Abfrage verknüpft. Dadurch kann die Struktur der Abfrage verändert und schädlicher Code eingeschleust werden.

Eine typische XPath-Abfrage ist eine Textzeichenfolge, die angibt, welche Elemente aus XML-Daten ausgewählt werden sollen.

Im obigen Code wählt die Abfrage "/users/userinfo[username='" + username + "' and password='" + password + "']" alle userinfo-Elemente aus, die mit dem vom Nutzer angegebenen Benutzernamen und Passwort übereinstimmen.

Enthalten ein Benutzername und ein Passwort jedoch Sonderzeichen oder Schlüsselwörter, die in XPath-Abfragen verwendet werden, verändert dies die Struktur der Abfrage und ermöglicht einem Angreifer, das Anmeldesystem zu umgehen. Werden beispielsweise für Benutzername und Passwort jeweils "' or 1=1" eingegeben, ergibt sich die folgende XPath-Abfrage:

//users/userinfo[username = '' or 1=1' and password = '' or 1=1' ]

Diese Abfrage wählt alle userinfo-Elemente mit einem username- und einem password-Attribut aus, die entweder leer oder gleich "1=1" sind. Der logische Ausdruck "1=1" ergibt unabhängig von den tatsächlichen Werten der Attribute username und password immer true.

Angenommen, ein Angreifer möchte sich ohne gültigen Benutzernamen oder gültiges Passwort Zugriff verschaffen. Statt gültiger Anmeldedaten könnte er im Anmeldeformular für Benutzername und Passwort jeweils eine der folgenden XPath-Injections eingeben:

  • 'or'1'='1

  • ' or 1=1 or 'a'='a'

  • ' or ''=''

  • ' or '1'='1'

  • 'a' or true() or '

  • 'text' or '1' = '1'

Jede der obigen Anweisungen umgeht die Anwendungssicherheit erfolgreich, indem sie die Logik und Struktur von XPath-Abfragen ausnutzt: Alle aus diesen Eingaben gebildeten XPath-Abfragen ergeben immer „true“. Wenn Angreifer außerdem einen Benutzernamen kennen oder erraten können (z. B. kimkimani), können sie die folgende Abfrage ausführen:

kimkimani' or '1'='1

Wenn wir diese Abfrage als username und password eingeben, wählt die resultierende Abfrage das userinfo-Element mit username kimkimani, jedes userinfo-Element namens kimkimani oder '1'='1' sowie jedes userinfo-Element aus, bei dem der Wert des Attributs username '1'='1' und der Wert des Passwortattributs '1'='1' ist. Da alle userinfo-Elemente diese Bedingungen erfüllen, kann sich der Angreifer beim Anmeldesystem anmelden.

Je nach Struktur des XML-Dokuments kann ein Angreifer außerdem eine Injection senden, die auf den Positionen der Knoten basiert:

'or position()=4 or'

Mit dieser Abfrage können Angreifer die Authentifizierung umgehen, indem sie die Position des Nutzers in der Struktur des XML-Dokuments ausnutzen. Die Funktion position() ist eine integrierte XPath-Funktion, die die Position des aktuellen Knotens relativ zu seinem übergeordneten Knoten zurückgibt. Der obige Ausdruck ergibt für den vierten Knoten im XML-Dokument true und ermöglicht dem Angreifer so die Anmeldung.

XPath-Injection-Schwachstellen verhindern

Es gibt zahlreiche Möglichkeiten, unsichere XML-Daten auszunutzen. Gleichzeitig stehen uns verschiedene Methoden zur Verfügung, um diese Schwachstellen in unseren Websites und Anwendungen zu entschärfen. Sehen wir uns einige davon an.

Eingaben bereinigen

Eine Strategie zur Verhinderung von XPath-Injection-Angriffen besteht darin, die Zeichen zu filtern, die Nutzer eingeben können. Diese Methode ist zwar nicht narrensicher, stellt aber sicher, dass Eingaben blockiert werden, aus denen sich Injection-Abfragen bilden ließen.

Erstellen Sie zur Umsetzung dieses Ansatzes die Datei regex.py im Ordner xpath und fügen Sie den folgenden Code ein.

# Import the required modules
from lxml import etree
import re

# Parse the XML data
tree = etree.parse("users_info.xml")

# Create an XPath evaluator
evaluator = etree.XPathEvaluator(tree, namespaces=None)

# login function
def login(username, password):
    if len(re.findall('[^a-zA-Z0-9]', username)) > 0 or ⏎ len(re.findall('[^a-zA-Z0-9]', password))>0:
        print("Suspected characters detected! Login failed!")
        return False
    else:
        expression = "/users/userinfo[username='" + username + "' and password='" +⏎  password + "']"
        results = tree.xpath(expression)

        # if no results, which means login details did not match
        if len(results) < 1:
            print("Incorrect login details!")

        # if login details match
        else:
            for result in results:
                print("Login successful for user", result[1].text)

login(username="kimkimani", password="6wre1678")

Dieser Code verwendet einen regulären Ausdruck (Regex), um alle Zeichen zu erkennen, die nicht numerisch oder alphabetisch sind. Wird eines dieser Zeichen erkannt, erscheint im Terminal die Meldung „Anmeldung fehlgeschlagen“, und wir geben sofort False an die aufrufende Funktion zurück, um die weitere Codeausführung zu verhindern. Wir können den Code mit den Zeichen testen, die für die im vorherigen Abschnitt erläuterte XPath-Injection verwendet wurden.

Dieser Ansatz erhöht zwar die Datensicherheit, doch bei der Eingabevalidierung werden nur die Zeichen herausgefiltert, an die wir beim Einfügen in die Bereinigungsfunktionen gedacht haben. Außerdem sollten Passwörter Sonderzeichen enthalten. Deshalb möchten wir solche Eingaben nicht blockieren.

Sehen wir uns daher einige alternative Methoden an, um XPath-Injection-Schwachstellen zu vermeiden.

Parametrisierte (vorbereitete) XPath-Abfragen verwenden

Erstellen Sie zur Veranschaulichung dieses Ansatzes die Datei params.py im Ordner xpath und fügen Sie den folgenden Code ein:

# Import the required modules
from lxml import etree

# Parse the XML data
tree = etree.parse("users_info.xml")

def login(username, password):
    expression = "/users/userinfo[username=$username and password=$password]"

    results = tree.xpath(expression, username=username, password=password)

    # if no results, which means login details did not match
    if len(results) < 1:
        print("Invalid credentials! Check your username or password.")
        return False
    # if login details match
    else:
        for result in results:
            print(f'Login successful! username: {result[1].text}, password:⏎ {result[2].text}')

login(username="kimkimani", password="6wreD1678")

Mit parametrisierten XPath-Abfragen vermeiden wir, Nutzereingaben mit der XPath-Abfrage zu verknüpfen, und übergeben sie stattdessen als Parameter. Parametrisierte XPath-Abfragen sind nicht nur sicherer, sondern auch flexibler und wiederverwendbar. Sie verhindern jedoch nicht alle Injection-Angriffe. Sehen wir uns deshalb eine weitere Schutzmaßnahme an.

Vorkompilierte XPath-Abfragen verwenden

Vorkompilierte XPath-Abfragen werden nicht aus von Nutzern bereitgestellten Daten erstellt. Daher sind sie die einzige vollständig sichere Methode, Injection-Angriffe zu verhindern. Um zu sehen, wie vorkompilierte XPath-Abfragen helfen, erstellen Sie eine Datei namens compiled.py und fügen Sie diesen Code ein:

import lxml.etree as et

xml = et.parse("users_info.xml")
find = et.xpath("/users/userinfo[username=$username and password=$password]")
results = find(xml, username="'or'1'='1", password="'or'1'='1")
print(results)

Der Code verwendet die Funktion etree.xpath, um eine XPath-Abfrage zu kompilieren, die anschließend in der Variable find gespeichert wird. Mit der Funktion find können wir diese kompilierte Abfrage mehrfach ausführen, ohne sie jedes Mal neu kompilieren zu müssen. Diese Option ist am effektivsten, da wir uns keine Gedanken darüber machen müssen, welche Zeichen wir hätten maskieren sollen.

Best Practices zum Erkennen und Vermeiden von XPath-Injection-Schwachstellen

Neben den oben genannten Schutzmaßnahmen, von denen vorkompilierte Abfragen die Best Practice darstellen, sollten wir die folgenden Best Practices umsetzen, um XPath-Injection-Schwachstellen zu erkennen und zu vermeiden.

Pakete und Code auf Schwachstellen überprüfen

Verwenden Sie ein Tool wie Snyk Open Source Advisor, um die von uns verwendeten Open-Source-Pakete auf potenzielle XPath-Injection-Schwachstellen zu untersuchen. Snyk Advisor bietet einen umfassenden Überblick über den Zustand der Pakete und gibt uns mehr Sicherheit hinsichtlich des Schutzes unserer Web-App.

Ebenso können wir Snyk Code für Quellcode-Scans verwenden, um potenzielle Sicherheitslücken, einschließlich XPath-Injections, zu erkennen. Snyk Code erkennt mögliche Schwachstellen in unserem Code schnell und unkompliziert und unterstützt uns dabei, sie zu beheben.

Der folgende Screenshot zeigt die Ergebnisse eines Quellcode-Scans des Projektordners mit Snyk Code.

Terminalausgabe eines Snyk-Code-Tests mit einer XPath-Injection-Sicherheitslücke mit hohem Schweregrad in main.py

Nutzereingaben nicht mit XPath-Abfragen verknüpfen

Fügen Sie von Nutzern bereitgestellte Eingaben nicht direkt durch Verkettung in XPath-Abfragen ein. Dies ist eine der häufigsten Ursachen für XPath-Injection-Schwachstellen. Mit parametrisierten XPath-Abfragen können Sie stattdessen Platzhalter für Nutzereingaben verwenden. Dadurch wird der Code lesbarer und wartungsfreundlicher. Außerdem lassen sich Nutzereingaben so leichter bereinigen und XPath-Injection-Angriffe verhindern.

Auf Sonderzeichen achten

Wenn Sie keine parametrisierten Abfragen verwenden können, müssen Sie Sonderzeichen in von Nutzern bereitgestellten Eingaben korrekt maskieren, bevor Sie sie in einer XPath-Abfrage verwenden.

Prepared Statements und Bind-Variablen verwenden

Prepared Statements und Bind-Variablen in XPath-Abfragen können verhindern, dass ein Angreifer beliebige XPath-Abfragen in die Eingabe einer Anwendung einschleust. Die Eingabe wird dabei als Variablenwert behandelt und nicht als Teil der Abfrage selbst.

Code regelmäßig überprüfen

Neben Tools wie Snyk Advisor und Snyk Code ist es wichtig, den Code regelmäßig auf potenzielle XPath-Injection-Schwachstellen zu überprüfen und zu testen. So können Sie mögliche Schwachstellen erkennen und beheben, bevor Angreifer sie ausnutzen. Nutzen Sie manuelle Code-Reviews, automatisierte Codeanalyse-Tools wie Snyk Code und Penetrationstests, um Schwachstellen im Code zu erkennen und zu beheben.

Fazit

XPath-Injections können Websites, Daten und den Ruf von Unternehmen erheblich schädigen und das Risiko künftiger Sicherheitsverletzungen erhöhen. Mit etwas zusätzlichem Aufwand können wir unsere Anwendungen jedoch schützen: durch Tools wie Snyk Advisor und Snyk Code, Best Practices zum Erkennen und Vermeiden von XPath-Injection-Schwachstellen in unserem Code sowie sorgfältiges Patchen von Schwachstellen.

Wir können unsere Anwendungen vor XPath-Injection-Angriffen schützen, indem wir von Nutzern bereitgestellte Eingaben korrekt bereinigen, parametrisierte XPath-Abfragen verwenden und insbesondere vorkompilierte XPath-Abfragen einsetzen. Da diese nicht aus von Nutzern bereitgestellten Daten erstellt werden, sind sie die einzige vollständig sichere Methode, Injection-Angriffe zu verhindern. Angesichts der möglichen Folgen von XPath-Injection-Angriffen dürfen diese Schutzmaßnahmen nicht vernachlässigt werden.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.