Richtlinien

Injection – Path Traversal

Path Traversal ist eine weitere recht verbreitete Art der Injection-Schwachstelle. Sie tritt meist dann auf, wenn bei der Erstellung eines URI (sei es für eine URL, einen Dateipfad oder Ähnliches) nicht ordnungsgemäß sichergestellt wird, dass der vollständig aufgelöste Pfad nicht außerhalb des beabsichtigten Stammverzeichnisses liegt.

Es ist wichtig zu erwähnen, dass Path Traversal eigentlich auch als eine Form der Pfad-*Injection*-Schwachstelle betrachtet werden kann.

Die Auswirkungen einer Pfad-Traversal-Schwachstelle hängen stark vom Kontext ab, in dem die Traversal auftritt, sowie von der allgemeinen Absicherung des Systems. Bevor wir jedoch darauf eingehen, lassen Sie uns anhand eines kurzen Praxisbeispiels verdeutlichen, worum es bei dieser Schwachstelle geht:

Eine kurze Analyse

Stellen Sie sich einen Endpunkt in Ihrer Anwendung vor, der Dokumente bereitstellt, wie etwa Vorlagen für Verträge oder Stellenangebote. Dabei kann es sich um statische Dateien in Ihrer Anwendung handeln, beispielsweise PDFs.

In diesem Fall könnte ein Code-Abschnitt zum Abrufen der Dateien bei einer Anfrage wie folgt aussehen:

let baseFolder = "/var/www/api/documents/";
let path = baseFolder + request.params.filename;

return file.read(path);

Um zu demonstrieren, wie die Schwachstelle ausgenutzt wird, müssen wir wissen, wo sich das Stammverzeichnis unserer Anwendung befindet. Nehmen wir für dieses Beispiel an, das Root-Verzeichnis der Anwendung liegt unter „/var/www/api/“.

Wir wissen, dass die Anwendung einen „filename“-Parameter entgegennimmt. Schauen wir uns einige Beispiele für Eingaben und deren Ergebnisse an:

Filename Unresolved path Resolved path
Privacy.pdf /var/www/api/documents/Privacy.pdf /var/www/api/documents/Privacy.pdf
../config/prod.config /var/www/api/documents/../config/prod.config /var/www/api/config/prod.config
../../../../etc/shadow /var/www/api/documents/../../../../etc/shadow /etc/shadow

Beachten Sie, wie wir mithilfe von „../“ durch das Dateisystem navigieren können. Wir sind in der Lage, den „documents“-Ordner, in dem die PDFs normalerweise liegen, zu verlassen und in den „/etc/“-Ordner zu gelangen, der die „shadow“-Datei enthält – diese speichert unter Linux Passwort-Hashes. Wie Sie sich vorstellen können, ist das alles andere als ideal.

Traversal in URLs betrachten

Eine weitere Variante der Pfad-Traversal kann auftreten, wenn URLs erstellt werden, die mit einer API interagieren sollen. Angenommen, wir haben eine API mit den folgenden Methoden:

URL pattern Description
/api/v1/order/get/{id} Gets details about the order with the specified ID
/api/v1/order/delete/{id} Deletes an order with the specific ID

Die API wird von einer anderen Anwendung aufgerufen, die beispielsweise Informationen zu einer Bestellung abrufen möchte:

let apiBase = "https://my.api/api/v1";
let orderApi = apiBase + "/order/get";

let apiUrl = orderApi + request.params.orderId;

let response = http.get(apiUrl);

Was passiert nun, abhängig von der vom Benutzer bereitgestellten Bestell-ID? Unten sehen Sie die effektive URL, die basierend auf der bereitgestellten Eingabe aufgerufen wird.

Die Normalisierung (Canonicalization) erfolgt normalerweise nicht auf der Client-Seite (obwohl dies möglich wäre), aber Webserver normalisieren die Anfrage in das unten gezeigte Format.

Order ID number Actual URL invoked
1 /api/v1/order/get/1
1/../../delete/1 /api/v1/order/delete/1

Bei der Eingabe im zweiten Beispiel rufen wir nicht die Bestellung mit der ID „1“ ab, sondern führen stattdessen die Löschmethode aus, was natürlich dazu führt, dass die Bestellung gelöscht wird.

Gegenmaßnahmen

Bei der Behandlung von Pfad-Traversal-Schwachstellen gibt es sowohl direkte Abhilfemaßnahmen als auch indirekte Verteidigungstechniken, die so oft wie möglich angewendet werden sollten. Sehen wir uns zunächst an, wie man mit Pfaden umgeht.

Direkte Abhilfemaßnahme

Beim Umgang mit Pfaden müssen wir den Prozess der Pfadauflösung, auch Pfad-Kanonisierung genannt, und dessen Bedeutung verstehen.

Ein Pfad wie „/var/www/api/documents/../../../../etc/shadow“ ist ein nicht-kanonischer Pfad. Wenn Sie diesen Pfad bei Ihrem Dateisystem anfordern, wird er zu „/etc/shadow“ kanonisiert. Es ist entscheidend, dass Sie nicht versuchen, nicht-kanonische Pfade zu öffnen. Stattdessen sollten Sie Pfade zuerst kanonisieren, überprüfen, ob sie nur auf die beabsichtigte Datei oder den beabsichtigten Ordner verweisen, und sie erst dann lesen.

‍

let baseFolder = "/var/www/api/documents/";
let path = baseFolder + request.params.filename;

let resolvedPath = path.resolve(path);

if(!resolvedPath.startswith(baseFolder))
return "Versuch, außerhalb des Basisordners zu lesen";
else
return file.read(resolvedPath);

Anti-Pattern – Versuch, Dateinamen zu bereinigen

Es mag verlockend sein, so etwas zu tun:


let baseFolder = "/var/www/api/documents/";
let path = baseFolder + request.params.filename.replace("../", "");
...

Dieser Ansatz sollte jedoch nicht verwendet werden. Der Schlüssel beim Umgang mit Pfaden liegt darin, immer den kanonischen Pfad zu betrachten.

Solange der kanonische Pfad keine Regeln verletzt, spielt es keine Rolle, wie der Pfad letztendlich konstruiert ist. Der Versuch, einen Pfad auf diese Weise zu bereinigen, ist sehr fehleranfällig und selten, wenn überhaupt, sicher.

Zugriff einschränken

In unseren vorherigen Beispielen haben wir das Lesen der Datei „/etc/shadow“ verwendet, die unter Linux die Passwort-Hashes enthält. Es gibt jedoch keinen Grund, warum eine Anwendung in der Lage sein sollte, diese Datei oder andere Dateien außerhalb ihres Stammverzeichnisses zu lesen.

Wenn Sie Container einsetzen, mindern Sie wahrscheinlich bereits viele Risiken. Es ist wichtig, Schritte zur Härtung des Containers zu unternehmen (z. B. nicht als Root ausführen). Es wird dringend empfohlen, alle Privilegien Ihres Webprozesses zu entziehen und seine Leseberechtigungen im Dateisystem auf die Dateien zu beschränken, die er unbedingt benötigt.

Beispiele

Wir werden nun einige Beispiele in verschiedenen Sprachen teilen, um die Funktionsweise besser zu veranschaulichen.

C# – Unsicher

Wenn der vollständige Pfad nicht aufgelöst wird oder nicht sichergestellt ist, dass nur der Dateiname eines Pfads verwendet wird, ist der Code anfällig für Path Traversal.

var baseFolder = "/var/www/app/documents/";
var fileName = "../../../../../etc/passwd";

// UNSICHER: Liest /etc/passwd
var fileContents = File.ReadAllText(Path.Combine(baseFolder, fileName));

C# – Sicher – kanonisch

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir den vollständigen (absoluten) Pfad auflösen und sicherstellen, dass der aufgelöste Pfad innerhalb unseres Basisordners liegt.

var baseFolder = "/var/www/app/documents/";
var fileName = "../../../../../etc/passwd";

var canonicalPath = Path.GetFullPath(Path.Combine(baseFolder, fileName));

// SICHER: Lehnt jeden Versuch ab, außerhalb der angegebenen Basis zu lesen.
if(!canonicalPath.StartsWith(baseFolder))
return "Versuch, Datei außerhalb des Basisordners zu lesen";

var fileContents = File.ReadAllText(canonicalPath);

C# – Sicher – Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamen aus dem Pfad verwenden. Dadurch ist es unmöglich, den angegebenen Ordner zu verlassen.

var baseFolder = "/var/www/app/documents/";

// Nur verwenden, wenn das Navigieren in Unterordner nicht erlaubt ist
var fileName = Path.GetFileName("../../../../../etc/passwd");

// SICHER: Liest /var/www/app/documents/passwd
var fileContents = File.ReadAllText(Path.Combine(baseFolder, fileName));

Java - Unsicher

Wenn der vollständige Pfad nicht aufgelöst wird oder nicht sichergestellt ist, dass nur der Dateiname eines Pfads verwendet wird, ist der Code anfällig für Path Traversal.

String baseFolder = "/var/www/app/documents/";
String fileName = "../../../../../etc/passwd";

// UNSICHER: Liest /etc/passwd
Path filePath = Paths.get(baseFolder + fileName);
List<String> lines = Files.readAllLines(filePath);

Java – Sicher – Kanonisch

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir den vollständigen (absoluten) Pfad auflösen und sicherstellen, dass der aufgelöste Pfad innerhalb unseres Basisordners liegt.

String baseFolder = "/var/www/app/documents/";
String fileName = "../../../../../etc/passwd";

// UNSICHER: Liest /etc/passwd
Path normalizedPath  = Paths.get(baseFolder + fileName).normalize();
if(!normalizedPath.toString().startsWith(baseFolder))
{
    return "Versuch, auf einen Pfad außerhalb des Root-Verzeichnisses zuzugreifen";
}
else
{
    List<String> lines = Files.readAllLines(normalizedPath);
}

Java – Sicher – Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamen aus dem Pfad verwenden. Dadurch ist es unmöglich, den angegebenen Ordner zu verlassen.

String baseFolder = "/var/www/app/documents/";

// Nur verwenden, wenn das Navigieren in Unterordner nicht erlaubt ist
String fileName = Paths.get("../../../../../etc/passwd").getFileName().toString();

// SICHER: Liest /var/www/app/documents/passwd
Path filePath = Paths.get(baseFolder + fileName);
List<String> lines = Files.readAllLines(filePath);

Javascript – Unsicher

Wenn der vollständige Pfad nicht aufgelöst wird oder nicht sichergestellt ist, dass nur der Dateiname eines Pfads verwendet wird, ist der Code anfällig für Path Traversal.

const fs = require('fs');

const baseFolder = "/var/www/app/documents/";
const fileName = "../../../../../etc/passwd";

// UNSICHER: Liest /etc/passwd
const data = fs.readFileSync(baseFolder + fileName, 'utf8');

Javascript – Sicher – Kanonisch

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir den vollständigen (absoluten) Pfad auflösen und sicherstellen, dass der aufgelöste Pfad innerhalb unseres Basisordners liegt.

const fs = require("fs");
const path  = require("path");

const baseFolder = "/var/www/app/documents/";
const fileName = "../../../../../etc/passwd";

const normalizedPath = path.normalize(path.join(baseFolder, fileName));

// SICHER: Liest /var/www/app/documents/passwd
const data = fs.readFileSync(normalizedPath, 'utf8');

Javascript – Sicher – Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamen aus dem Pfad verwenden. Dadurch ist es unmöglich, den angegebenen Ordner zu verlassen.

const fs = require("fs");
const path  = require("path");

const baseFolder = "/var/www/app/documents/";
const fileName = path.basename("../../../../../etc/passwd");

// SICHER: Liest /var/www/app/documents/passwd
const data = fs.readFileSync(path.join(baseFolder, fileName), 'utf8');

Python – Unsicher

Wenn der vollständige Pfad nicht aufgelöst wird oder nicht sichergestellt ist, dass nur der Dateiname eines Pfads verwendet wird, ist der Code anfällig für Path Traversal.

baseFolder = "/var/www/app/documents/"
fileName = "../../../../../etc/passwd"

# UNSICHER: Liest /etc/passwd
fileContents = open(baseFolder + fileName).read()

Python – Sicher – Kanonisch

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir den vollständigen (absoluten) Pfad auflösen und sicherstellen, dass der aufgelöste Pfad innerhalb unseres Basisordners liegt.

import os.path

baseFolder = "/var/www/app/documents/"
fileName = "../../../../../etc/passwd"

normalizedPath =  os.path.normpath(baseFolder + fileName)

# SICHER: Blockiert jeden Versuch, Dateien außerhalb des angegebenen Basisordners zu lesen
if not normalizedPath.startswith(baseFolder):
    return "Versuch, außerhalb des Basisordners zu lesen"

# SICHER: Liest /var/www/app/documents/passwd
fileContents = open(normalizedPath).read()

Python – Sicher – Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamen aus dem Pfad verwenden. Dadurch ist es unmöglich, den angegebenen Ordner zu verlassen.

import os.path

baseFolder = "/var/www/app/documents/"
fileName = os.path.basename("../../../../../etc/passwd")

# SICHER: Liest /var/www/app/documents/passwd
fileContents = open(os.path.join(baseFolder,  fileName)).read()
Slogan

Sichere Programmierung in der Praxis

Entdecken Sie Richtlinien für sicheres Programmieren, um Schwachstellen wie die OWASP Top 10 zu verstehen und zu beheben, und tauchen Sie in geführte Trainingsmissionen ein, um diese in realistischen Anwendungssimulationen zu üben.

Weitere Richtlinien
Demo buchen