
Datei hochladen
Es kommt sehr häufig vor, dass Anwendungen irgendwann Benutzern erlauben müssen, eine Datei (entweder zur Verwendung oder nur zur Speicherung) irgendwo in der Anwendung hochzuladen. Es scheint zwar einfach zu sein, aber die Art und Weise, wie diese Funktion implementiert wird, kann aufgrund der potenziellen Risiken, die mit der Handhabung von Datei-Uploads verbunden sind, ziemlich wichtig sein.
Schauen Sie sich dieses kurze Beispiel an, um ein besseres visuelles Verständnis dessen zu vermitteln, was wir meinen.
Nehmen wir an, dies ist eine Anwendung, mit der Benutzer ein Profilbild hochladen können:
öffentliche Zeichenfolge uploadProfilePicture (formFile uploadedFile)
{
//Generieren Sie den Pfad zum Speichern der hochgeladenen Datei unter
var-Pfad = $“. /uploads/avatare/ {request.user.ID}/{uploadedFile.filename}“;
//Speichere die Datei
var LocalFile = file.openWrite (Pfad);
Lokaldatei.write (hochgeladene Datei.readToEnd ());
Lokaldatei.flush ();
Lokaldatei.close ();
//Aktualisiere das Profilbild
userProfile.updateUserProfilePicture (request.User, Pfad)
Rückweg;
}
Dies wäre eine sehr einfache Upload-Funktion, die zufällig auch für Path Traversal anfällig ist.
Abhängig von der genauen Implementierung der Anwendung könnte ein Angreifer eine weitere Seite/ein anderes Skript hochladen (denken Sie an ASP-, .aspx- oder .php-Dateien), das es ermöglichen würde, beliebigen Code direkt aufzurufen und auszuführen. Dies könnte auch das Überschreiben vorhandener Dateien ermöglichen.
Problem 1 — Speichern auf einer lokalen Festplatte statt auf einem externen Datenspeicher
Da die Nutzung von Cloud-Diensten immer alltäglicher wird, Anwendungen in Containern bereitgestellt werden, Hochverfügbarkeits-Setups sind zum Standard geworden, und die Praxis, hochgeladene Dateien auf die lokale Festplatte der Anwendung zu schreiben, sollte grundsätzlich um jeden Preis vermieden werden.
Dateien sollten nach Möglichkeit in eine Form des zentralen Speichers hochgeladen werden (Blockspeicher oder Datenbank). Dadurch können in diesem Fall ganze Klassen von Sicherheitslücken vermieden werden.
Problem 2 — Erweiterungen werden nicht validiert
In vielen Fällen, in denen eine Sicherheitslücke beim Hochladen von Dateien ausgenutzt wird, hängt dies von der Fähigkeit ab, eine Datei mit einer bestimmten Erweiterung hochzuladen. Daher ist es sehr ratsam, eine „Zulassungsliste“ von Erweiterungen für Dateien zu verwenden, die hochgeladen werden können.
Stellen Sie sicher, dass Sie die von Ihrer Sprache/Ihrem Framework bereitgestellten Methoden verwenden, um die Erweiterung der Datei abzurufen, um Probleme wie die Nullbyte-Injektion zu vermeiden.
Es mag auch verlockend sein, den Inhaltstyp des Uploads zu überprüfen, aber dadurch kann er sehr spröde werden, da die für bestimmte Dateien verwendeten Inhaltstypen von Betriebssystem zu Betriebssystem unterschiedlich sein können. Es sagt Ihnen auch nichts über die Datei selbst aus, da der Inhaltstyp lediglich eine Zuordnung zu einer Erweiterung ist.
Problem 3 — Pfaddurchquerung wird nicht verhindert
Ein weiteres häufiges Problem bei Datei-Uploads ist, dass sie in der Regel auch anfällig für Pfaddurchquerung sind. Das ist eine ganze Sache für sich, also anstatt zu versuchen, es hier zusammenzufassen, geben Sie die vollständige Richtlinie Pfaddurchquerung ein Blick.
Mehr Beispiele
Im Folgenden finden Sie einige weitere Beispiele für sichere und unsichere Datei-Uploads, die Sie sich ansehen können.
C# - Unsicher
öffentliche Zeichenfolge uploadProfilePicture (IFormFile UploadedFile)
{
//Generieren Sie den Pfad zum Speichern der hochgeladenen Datei unter
var-Pfad = $“. /uploads/avatare/ {request.user.ID}/{uploadedFile.filename}“;
//Speichere die Datei
var LocalFile = file.openWrite (Pfad);
Lokaldatei.write (hochgeladene Datei.readToEnd ());
Lokaldatei.flush ();
Lokaldatei.close ();
//Aktualisiere das Profilbild
userProfile.updateUserProfilePicture (request.User, Pfad)
Rückweg;
}
C# - Sicher
öffentliche Liste <string>AllowedExtensions = new () {„.png“, „.jpg“, „.gif"};
öffentliche Zeichenfolge uploadProfilePicture (IFormFile UploadedFile)
{
//HINWEIS: Die beste Option ist es, das Speichern von Dateien auf der lokalen Festplatte zu vermeiden.
var basePath = path.getFullPath (“. /uploads/avatare/ „);
//Verhindern Sie das Durchqueren von Pfaden, indem Sie den angegebenen Dateinamen nicht verwenden. Wird auch benötigt, um Dateinamenkonflikte zu vermeiden.
var newFileName = GenerateFileName (UploadedFile.FileName);
//Generieren Sie den Pfad zum Speichern der hochgeladenen Datei unter
var canonicalPath = Path.Combine (BasePath, neuer Dateiname);
//Stellen Sie sicher, dass wir nicht versehentlich in einen Ordner außerhalb des Basisordners gespeichert haben
wenn (! Kanonischer Pfad. Beginnt mit (BasePath)
{
return badRequest („Es wurde versucht, die Datei außerhalb des Upload-Ordners zu speichern“);
}
//Stellen Sie sicher, dass nur erlaubte Erweiterungen gespeichert werden
wenn (! IsFileAllowed Extension (Uploaded Allowed Extensions)
{
gibt badRequest zurück („Erweiterung ist nicht erlaubt“);
}
//Speichere die Datei
var LocalFile = file.openWrite (CanonicalPath);
Lokaldatei.write (hochgeladene Datei.readToEnd ());
Lokaldatei.flush ();
Lokaldatei.close ();
//Aktualisiere das Profilbild
userProfile.updateUserProfilePicture (request.User, canonicalPath)
Rückweg;
public bool generateFileName (string originalFileName) {
gib $ "{guid.newGuid ()} {path.getExtension (OriginalFileName)}“ zurück;
}
<string>public bool isFileAllowedExtension (string fileName, Listenerweiterungen) {
gibt Extensions.contains (Path.getExtension (FileName)) zurück;
}