Moin!
Dann erklärst du uns jetzt am besten mal, warum PHP bei move_uploaded_file() intern diese Dinge tut, die sich allesamt so lesen, als würden dort explizit Checks durchgeführt, die einen erfolgten Dateiupload prüfen:
Du wirst jetzt hoffentlich auch Verständnis dafür haben, wenn ich mir jetzt auch ein paar Tage (Wochen?) Zeit nehmen werde, zumal Du ja nur die Frage aufwirfst, ob da eventuell Kontrollen vorgenommen werden, aber leider nicht mit der z.B. für Christian Seiler üblichen Klarheit beschreibst, welche Kontrollen da in welcher Form vorgenommen werden...
Das Problem ist:
Du erklärst in gewisser Selbstherrlichkeit die Funktion move_uploaded_file() für obsolet, im Widerspruch zur geschriebenen Dokumentation und der herrschenden Meinung, aber du bietest keinerlei Alternative an, wie das, was die Funktion tut, nach deiner Meinung zu realisieren sei.
Die übliche Vorgehensweise bei RFCs ist, dass ein altes RFC nur durch ein neues RFC für obsolet erklärt werden kann, indem die bisherige Vorgehensweise durch eine bessere Vorgehensweise ersetzt wird. Das fehlt hier. Und insbesondere dein Verweis auf den Wiki-Artikel, welcher ausgerechnet DIESEN Punkt derzeit nicht mit Inhalt füllt, klingt dann doch eher wie Ironie.
Was die Kontrollen innerhalb der Funktion angeht (ich spekuliere mal etwas aufgrund des vorliegenden Codes, der mir ja trotz allem etwas sagen will):
Offensichtlich pflegt PHP im Upload-Fall eine Datenstruktur "rfc1867_uploaded_files", in der alle hochgeladenen Dateien vermerkt werden.
move_uploaded_file() bricht ab, wenn im Skriptrequest keine Dateien hochgeladen wurden (Zeile 5751).
Es bricht ab, wenn nicht genau zwei Funktionsparameter verwendet werden (Zeile 5755).
Es bricht ab, wenn der erste Funktionsparameter nicht in der Liste der hochgeladenen Dateien steht (Zeile 5759).
Es bricht ab, wenn der Zielpfad durch open_basedir-Restriktionen nicht erlaubt ist (Zeile 5763).
Erst jetzt wird das Bewegen der Datei durchgeführt. Dabei wird die eventuell schon existierende Zieldatei in jedem Fall gelöscht, und dann durch den Versuch des Umbenennens innerhalb des gleichen Dateisystems "schnell" bewegt. Wenn das funktioniert, werden in Nicht-Windows-Versionen von PHP noch die Rechte der Zieldatei nachgezogen. Wenn Rename nicht funktioniert, wird versucht, die Datei auf das Ziel zu kopieren. Ist die Kopie erfolgreich, wird die Quelldatei gelöscht.
Wenn der vorgenannte Vorgang erfolgreich war, wird der Quellpfad der Datei aus der Liste hochgeladener Dateien gelöscht. Andernfalls gibts eine Warnung.
Ich denke, die Behauptung der Funktionsdokumentation, dass move_uploaded_file() eine Datei nur dann verschiebt, wenn diese aus einem HTTP-Upload stammt, und dies auch nur ein einziges Mal pro Skriptausführung und Dateiname erlaubt, ergibt sich aus dem dargelegten Code recht deutlich.
Ich müsste jetzt also als Gegenbeweis die konsequente Kontrollverfolgung bis zum $_FILES-Array hin heraussuchen und auch für Leute, wie ChrisB verständlich, wiedergeben. Das ist nicht einfach!
Deine Argumentation klingt eher wie "Ich muss einen String, der nur Integer-Werte enthält, ja nicht mit htmlspecialchars() behandeln, wenn ich ihn in HTML reintun will, weil damit ja sowieso nix geändert wird. htmlspecialchars() ist zum Escapen von Integer-Strings obsolet."
Ich denke, deine Suche im Code wird nicht sonderlich von Erfolg gekrönt sein, denn das Nadelöhr ist die Funktion zum Bewegen der Datei. PHP bietet nur zwei Funktionen an, die das überhaupt erledigen, einerseits move_uploaded_file(), andererseits rename(). Kennst du noch andere?
Was den Sicherheitsaspekt angeht, kann rename() nicht mithalten. Denn selbst wenn der Quellpfad der zu verschiebenden Datei tatsächlich auf sichere Weise aus $_FILES ermittelt wurde, so muss rename() ja dennoch für den allgemeinen Fall geschrieben sein. Es kann also nicht geprüft werden, ob die zu verschiebende Datei hochgeladen wurde. Ebenso kann nicht geprüft werden, ob mehrfach versucht wird, die Datei zu verschieben.
Abgesehen davon verrät die Doku, dass rename() erst seit PHP Version 5.3.1 in der Lage ist, auch unter Windows Dateien zwischen verschiedenen Dateisystemen zu verschieben.
Unter diesen Gesichtspunkten sieht es für die Nutzung der Funktion rename() also ganz schlecht aus:
1. Unter Windows muss man vor PHP 5.3.1 eine Kombination aus copy() und unlink() programmieren, wenn man die Datei z.B. zwischen zwei Laufwerken verschieben will. Anzumerken sei, dass auch NTFS das Mounten von Partitionen in Unterverzeichnissen erlaubt, d.h. man kann nicht anhand des Quell- und Zielpfads erkennen, ob rename() oder copy/unlink() erforderlich ist, sondern muss rename() probieren und im Fehlerfall copy/unlink() nachziehen.
2. rename() hat keine zusätzliche Schutzschicht, die verhindert, dass Dateien verschoben werden, die nicht hochgeladen wurden.
3. rename() verhindert auch nicht, dass versucht wird, eine hochgeladene Datei zweimal zu verschieben.
4. rename() kann die verschobene Datei auch nicht aus der Liste der hochgeladenen Dateien entfernen - PHP wird also am Skriptende versuchen, die verschobene Datei an ihrem alten Ort zu löschen.
Würdest du also immer noch argumentieren, move_uploaded_file() ist unter diesem Gesichtspunkten obsolet?
- Sven Rautenberg