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