英雄背景无分隔线
指导方针

Injektion — Pfaddurchquerung

Path Traversal ist eine weitere ziemlich häufige Art von Injektionsanfälligkeit. Sie treten in der Regel auf, wenn durch die Erstellung einer URI (sei es für eine URL, einen Dateipfad oder etwas anderes) nicht ordnungsgemäß sichergestellt wird, dass der vollständig aufgelöste Pfad nicht außerhalb des Stammverzeichnisses von vorgesehen Pfad.

Es ist wichtig, darauf hinzuweisen, dass das Durchqueren von Pfaden auch als Sicherheitslücke mit Pfad*injektion* angesehen werden könnte.

Die Auswirkungen einer Pfaddurchquerungsanfälligkeit hängen stark vom Kontext ab, in dem die Traversierung stattfindet, und von der allgemeinen Absicherung, die vorgenommen wurde. Aber bevor wir darauf eingehen, schauen wir uns ein kurzes praktisches Beispiel für diese Sicherheitslücke an, um zu sehen, wovon wir sprechen:

Eine schnelle Panne

Erwägen Sie einen Endpunkt in Ihrer Bewerbung, der Dokumente wie Vorlagen für Verträge oder Stellenangebote bereitstellt. Dies können alles Dateien wie PDFs sein, die in Ihrer Bewerbung statisch sind.

In dieser Situation haben Sie möglicherweise einen Code wie diesen, um die Dateien auf Anfrage abzurufen:

lass BaseFolder = „/var/www/api/documents/“;
let path = BaseFolder + request.params.filename;

gib file.read (Pfad) zurück;

Um zu demonstrieren, wie sich die Sicherheitsanfälligkeit auswirkt, müssen wir auch wissen, wo sich die Wurzel unserer Anwendung befindet. Nehmen wir für dieses Beispiel an, dass sich das Stammverzeichnis der Anwendung unter '/var/www/api/' befindet.

Wir wissen, dass die Anwendung einen 'Dateinamen'-Parameter verwendet. Schauen wir uns ein paar Beispiele für Eingaben an und was das Ergebnis ist:

文件名 未解决的路径 已解决的路径
隐私.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 das Dateisystem mit '.. /' durchqueren können. Wir können aus dem Ordner „Dokumente“, in dem sich die PDFs normalerweise befinden, in den Ordner „/etc/“ wechseln, der die „Shadow“ -Datei enthält, die unter Linux Passwort-Hashes enthält. Wie Sie sich vorstellen können, ist das wirklich nicht ideal.

Blick auf Traversal in Urls

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

URL 模式 说明
/api/v1/order/get/{id} 获取指定 ID 订单的详细信息
/api/v1/order/delete/{id} 删除具有特定 ID 的订单

Mit der API interagiert eine andere Anwendung, die sie beispielsweise aufrufen könnte, wenn versucht wird, Informationen über eine Bestellung abzurufen:

lass apiBase = "https://my.api/api/v1 „;
let orderAPI = ApiBase + „/order/get“;

lass apiUrl = orderApi + request.params.orderId;

lass response = http.get (apiUrl);

Was passiert nun, abhängig von der vom Benutzer angegebenen Bestellnummer? Unten sehen Sie die effektive URL, die auf der Grundlage der bereitgestellten Eingabe aufgerufen wurde.

Die Kanonisierung erfolgt normalerweise nicht auf der Clientseite (obwohl dies der Fall sein kann), aber Webserver kanonisieren die Anfrage in das unten angegebene Format.

订单 ID 号 实际调用的 URL
1 /api/v1/order/get/1
1/.../.../delete/1 /api/v1/order/delete/1

Bei der Eingabe des zweiten Beispiels haben wir, anstatt die Bestellung mit der ID-Nummer '1' abzurufen, stattdessen die Methode delete aufgerufen, was natürlich zum Löschen der Bestellung führt.

Abhilfemaßnahmen

Wenn es um Pfadüberschreitung geht, gibt es sowohl direkte Abhilfemaßnahmen als auch indirekte/Abwehrtechniken, die so oft wie möglich angewendet werden können und sollten. Schauen wir uns zunächst an, wie man mit Pfaden umgeht.

Direkte Schadensbegrenzung

Wenn es darum geht, einen Pfad zu handhaben, müssen wir den Prozess der Pfadauflösung oder Pfadkanonisierung und seine Bedeutung verstehen.

Wenn Sie einen Pfad wie '/var/www/api/documents/../../../.. haben /etc/shadow ', es befindet sich in einem nicht-kanonischen Pfad. Wenn Sie diesen Pfad von Ihrem Dateisystem anfordern, wird es ihn in '/etc/shadow' kanonisieren. Es ist wichtig, dass Sie nicht versuchen, nicht-kanonische Pfade zu öffnen. Stattdessen sollten Sie zuerst Pfade kanonisieren, überprüfen, ob sie nur auf die gewünschte Datei oder den gewünschten Ordner verweisen, und ihn dann lesen.

lass BaseFolder = „/var/www/api/documents/“;
let path = BaseFolder + request.params.filename;

lass resolvedPath = path.resolve (path);

wenn (! Gelöster Pfad. Beginnt mit (BaseFolder))
return „Es wurde versucht, außerhalb des Basisordners zu lesen“;
sonst
gib file.read (resolvedPath) zurück;

Anti-Pattern - Es wird versucht, Dateinamen zu bereinigen

Es mag verlockend sein, so etwas zu tun:


lass BaseFolder = „/var/www/api/documents/“;
let path = BaseFolder + request.params.filename.replace („../“, „“);
...

Dieser Ansatz sollte jedoch nicht benutzt werden. Der Schlüssel beim Umgang mit Pfaden besteht darin, immer den kanonischen Pfad im Auge zu behalten.

Solange der kanonische Pfad keine Regeln verletzt, macht es keinen Unterschied, wie der Pfad letztendlich konstruiert wird. Der Versuch, einen Pfad wie diesen zu bereinigen, ist sehr fehleranfällig und selten sicher, wenn überhaupt.

Zugriff einschränken

In unseren vorherigen Beispielen haben wir das Lesen der Datei '/etc/shadow' verwendet, bei der es sich um die Datei mit Passwort-Hashes unter Linux handelt. Aber es gibt wirklich 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, Maßnahmen zur Härtung des Containers zu ergreifen (nicht als Root ausführen usw.). Es wird dringend empfohlen, Ihrem Webprozess alle Rechte zu entziehen und seine Leseberechtigungen im Dateisystem auf die Dateien zu beschränken, die er unbedingt benötigt.

Beispiele

Jetzt teilen wir ein paar Beispiele in verschiedenen Sprachen, um die Dinge ein wenig besser zu veranschaulichen, während sie in Aktion sind.

C# - Unsicher

Wenn Sie nicht den vollständigen Pfad auflösen oder sicherstellen, dass Sie nur den Dateinamensteil eines Pfads verwenden, ist der Code anfällig für Path Traversal.

var BaseFolder = „/var/www/app/documents/“;
var Dateiname = „../../../../.. /etc/passwd „;

//UNSICHER: Liest /etc/passwd
var fileContents = file.readAllText (Path.Combine (Basisordner, Dateiname));

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 sich der Pfad zur aufgelösten Datei in unserem Basisordner befindet.

var BaseFolder = „/var/www/app/documents/“;
var Dateiname = „../../../../.. /etc/passwd „;

var canonicalPath = path.getFullPath (Path.Combine (Basisordner, Dateiname));

//SECURE: Lehnt jeden Versuch ab, außerhalb der angegebenen Basis zu lesen.
wenn (! CanonicalPath.StartsWith (BaseFolder)
gibt „Es wird versucht, eine Datei außerhalb des Basisordners zu lesen“ zurück;

var fileContents = file.readAllText (kanonischer Pfad);

C# - Secure - Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamenanteil des Pfads verwenden und so sicherstellen, dass es unmöglich ist, den angegebenen Ordner zu verlassen.

var BaseFolder = „/var/www/app/documents/“;

//Benutze das nur, wenn du das Navigieren in andere Unterordner nicht erlaubst
var Dateiname = Path.getFileName („../../../../.. /etc/passwd „);

//SICHER: Liest /var/www/app/documents/passwd
var fileContents = file.readAllText (Path.Combine (Basisordner, Dateiname));

Java - Unsicher

Wenn Sie nicht den vollständigen Pfad auflösen oder sicherstellen, dass Sie nur den Dateinamensteil eines Pfads verwenden, ist der Code anfällig für Path Traversal.

Zeichenfolge BaseFolder = „/var/www/app/documents/“;
Zeichenfolge Dateiname = „../../../../.. /etc/passwd „;

//UNSICHER: Liest /etc/passwd
Pfad FilePath = Paths.get (BaseFolder + Dateiname);
<String>Zeilen auflisten = 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 sich der Pfad zur aufgelösten Datei in unserem Basisordner befindet.

Zeichenfolge BaseFolder = „/var/www/app/documents/“;
Zeichenfolge Dateiname = „../../../../.. /etc/passwd „;

//UNSICHER: Liest /etc/passwd
Pfad NormalizedPath = Paths.get (BaseFolder + Dateiname) .normalize ();
wenn (! NormalizedPath.toString () .startsWith (BaseFolder)
{
gibt „Es wird versucht, einen Pfad außerhalb von root zu lesen“ zurück;
}
sonst
{
<String>Zeilen auflisten = files.readAllLines (normalizedPath);
}

Java - Secure - Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamenanteil des Pfads verwenden und so sicherstellen, dass es unmöglich ist, den angegebenen Ordner zu verlassen.

Zeichenfolge BaseFolder = „/var/www/app/documents/“;

//Benutze das nur, wenn du das Navigieren in andere Unterordner nicht erlaubst
Zeichenfolge Dateiname = Paths.get („../../../../../.. /etc/passwd „) .getFileName () .toString ();

//SICHER: Liest /var/www/app/documents/passwd
Pfad FilePath = Paths.get (BaseFolder + Dateiname);
<String>Zeilen auflisten = files.ReadAllLines (FilePath);

Javascript - Unsicher

Wenn Sie nicht den vollständigen Pfad auflösen oder sicherstellen, dass Sie nur den Dateinamensteil eines Pfads verwenden, ist der Code anfällig für Path Traversal.

const fs = benötigen ('fs');

const BaseFolder = „/var/www/app/documents/“;
const FileName = „../../../../.. /etc/passwd „;

//UNSICHER: Liest /etc/passwd
const data = fs.readFileSync (BaseFolder + Dateiname, '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 sich der Pfad zur aufgelösten Datei in unserem Basisordner befindet.

const fs = benötigen („fs“);
const path = require („Pfad“);

const BaseFolder = „/var/www/app/documents/“;
const FileName = „../../../../.. /etc/passwd „;

const normalizedPath = path.normalize (path.join (BaseFolder, Dateiname));

//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 Dateinamenanteil des Pfads verwenden und so sicherstellen, dass es unmöglich ist, den angegebenen Ordner zu verlassen.

const fs = benötigen („fs“);
const path = require („Pfad“);

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, Dateiname), 'utf8');

Python - Unsicher

Wenn Sie nicht den vollständigen Pfad auflösen oder sicherstellen, dass Sie nur den Dateinamensteil eines Pfads verwenden, ist der Code anfällig für Path Traversal.

BaseFolder = „/var/www/app/documents/“
Dateiname = „../../../../.. /etc/passwd“

# UNSICHER: Liest /etc/passwd
DateiInhalt = öffnen (BaseFolder + Dateiname) .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 sich der Pfad zur aufgelösten Datei in unserem Basisordner befindet.

importiere os.path

BaseFolder = „/var/www/app/documents/“
Dateiname = „../../../../.. /etc/passwd“

normalizedPath = os.path.normpath (BaseFolder + Dateiname)

# SECURE: Lehnt jeden Versuch ab, Dateien außerhalb des angegebenen Basisordners zu lesen
wenn nicht normalizedPath.startsWith (BaseFolder):
return „Es wird versucht, aus dem Basisordner auszulesen“

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

Python - Secure - Dateiname

In diesem Beispiel schützen wir uns vor Path Traversal, indem wir nur den Dateinamenanteil des Pfads verwenden und so sicherstellen, dass es unmöglich ist, den angegebenen Ordner zu verlassen.

importiere os.path

BaseFolder = „/var/www/app/documents/“
Dateiname = os.path.basename („../../../../.. /etc/passwd „)

# SICHER: Liest /var/www/app/documents/passwd
FileContents = open (os.path.join (BaseFolder, Dateiname)) .read ()