Zur Navigation

PHP-Upload [2]

11 lava

@marcel: diese "Ordnung" wollte ich an dieser Stelle definitiv nicht haben (bewußt nicht, nicht aus Unfähigkeit), ich wollte das Script so übersichtlich (so nackt) wie möglich halten, um erstmal die Basisfunktionen zu testen.


Ist der Password-Schutz aus Marcels Script das, was man normalerweise als "sicher genug" bezeichnet? Was würde eigentlich im worst case passieren, wenn jemand Unbefugtes Dateien auf meinen Host hochladen würde? Einfach nur, daß er seinen eigenen HTML-Kram hochladen könnte, oder auch schlimmere Sachen, die nicht nur meine Seiteninhalte, sondern auch den Host angreifen? D.h. würde ich mich nur selber ärgern oder zerstöre ich etwas mit (bzw. lasse Raum für Zerstörung), was mir gar nicht gehört?



Jörg hat mir ja meine Vermutung bestätigt (danke), daß ich es erstmal in dieser Form nicht testen kann, weil mein Server das nicht zuläßt. Da ich es gleich extern probiert hatte, probiere ich es jetzt spaßeshalber auch mal auf meinem lokalen XAMPP, aber da nutzt es mir natürlich nichts. Aber dort müßte ich ja wirklich alle Rechte selbst setzen können.....
Kann ich die Dateirechte auch auf dem Hostserverselber setzen? Wenn ich mich mit ftp einwähle, bin ich ja nicht gleich im Verzeichnis httpdocs, sondern hab httpdocs, httpsdocs, error-docs, cgi-bin, conf zur Auswahl ?
Gehostet wird meine Seite von einem entfernten Bekannten mit eigenem Server, der ist aber in Urlaub..... (Aber immerhin geht dort überhaupt PHP, was ja bei dem vorherigen Freehoster nicht ging...)



Kann mir bitte trotzdem noch jemand die Fragen aus meinem ursprünglichen Thread beantworten, ob meine jeweiligen Überlegungen prinzipiell korrekt waren.


---
Also, jetzt hab ich mein Script lokal getestet: Ich habe das Script scriptdatei.php aus dem Verzeichnis /opt/lampp/htdocs/Upload
lokal als http://localhost/Upload/scriptdatei.php aufgerufen
und dann versucht, das Bild drache.jpg in das Unterverzeichnis Pics einzutragen, das ich zuvor noch als /opt/lampt/htdocs/Upload/Pics
eingerichtet hatte. Die lokale Fehlermeldung nach Drücken des submit-Buttons war:

.... function.move-uploaded-file: failed to open stream:
Permission denied in /opt/lampp/htdocs/Upload/scriptdatei.php
(d.h. da muß ich noch Rechte setzen??? ist ja mein lokaler Rechner, wo ich mich als root einloggen kann - aber wie mach ich das?)
und
... function.mover-uploaded-file: Unable to move 'tmp/phpZSIAFp' to 'Pics/drache.jpg' in /opt/lampp/htdocs/Upload/scriptdatei.php
(na, da steht ja definitiv noch ein scriptdatei.php zuviel drin, also muß ich wohl irgendwo noch etwas ändern, damit die Einfügung von drache.jpg in /opt/lampp/htdocs/Upload/Pics (oder von Pics/drache.jpg in /opt/lampp/htdocs/Upload/ , ist das auch okay? jedenfalls ohne daß die aufrufende Datei mit im Pfad steht) gemacht wird.... was fehlt da? Wenn ich im Target noch ein "../". vor dem bisherigen Pfad einfüge, will er ../Pics/drache.jpg in /opt/lampp/htdocs/Upload/scriptdatei.php einfügen, das ist also auch offenbar nicht die Lösung....vielleicht gibt es ja ähnlich wie $_SERVER['PHP_SELF'] auch eine entsprechende superglobale Variable, die anzeigt, in welchem Verzeichnis das Script gerade läuft, wenn ja, müßte es damit gehen....
)

Muß aber jetzt erstmal zur Arbeit und komme frühestens heute abend/nacht wieder dazu....

04.01.2007 07:22 | geändert: 04.01.2007 08:32

12 Jörg Kruse

Zu den Sicherheitsaspekten:

Ist der Password-Schutz aus Marcels Script das, was man normalerweise als "sicher genug" bezeichnet?


Mir scheint es sicher. Testen kann man dies z.B., indem man falsche und keine Usernamwn bzw. Passwörter eingibt und nach jedem Schritt den Session-Cookie wieder löscht, um zu schauen, ob man auch ohne weiterkommt. Und die sicherheitsrelevante Variable $key kann von außen nicht überschrieben werden

Was würde eigentlich im worst case passieren, wenn jemand Unbefugtes Dateien auf meinen Host hochladen würde? Einfach nur, daß er seinen eigenen HTML-Kram hochladen könnte, oder auch schlimmere Sachen, die nicht nur meine Seiteninhalte, sondern auch den Host angreifen? D.h. würde ich mich nur selber ärgern oder zerstöre ich etwas mit (bzw. lasse Raum für Zerstörung), was mir gar nicht gehört?

Wenn es keine Einschränkung bei Mime Type und Dateiendung gibt, wäre dies ein großes Sicherheitsloch, da dann beliebige Scripte hochgeladen werden könnten. Wieweit man mit diesen Schaden auf dem Server anrichten kann, hängt von dessen Konfiguration ab - gegebenenfalls würden sich auch der Hoster und die Webspace-Nachbarn ärgern.

04.01.2007 13:13 | geändert: 04.01.2007 13:19

13 Marcel (Gast)

Nochmal kurz zur Sicherheit:

Ich hatte bisher mit meinem Script noch nie irgendwelche Probleme in Richtung Sicherheit. Wichtig ist allerdings, dass register_globals deaktiviert ist, da man sonst die Variable $key mit GET überschreiben kann und so dann ganz einfach in den geschützen Bereich kommt.

So gut kenne ich mich mit Upload-Scripten auch nicht aus, aber ich glaube die move-Funktion hat einen anderen Zweck.
Ich kenne es eigentlich nur mit Kopieren durch copy().

04.01.2007 15:35 | geändert: 04.01.2007 15:37

14 Jörg Kruse

Zitat von ***
Wichtig ist allerdings, dass register_globals deaktiviert ist, da man sonst die Variable $key mit GET überschreiben kann und so dann ganz einfach in den geschützen Bereich kommt.

Das ist bei diesem Scrip nicht der Fall, da $key ja initialisiert wurde:

$key = 0;
if ($_POST["username"] == "Username" AND $_POST["password"] == "Passwort") {
   $key = 1;
   $_SESSION["username"] = $_POST["username"];
   $_SESSION["password"] = $_POST["password"];
}

Ein Überschreiben ist dann auch mit register_globals = on nicht möglich

Ansonsten ist es aber natürlich nicht verkehrt, in der php.ini die Einstellung register_globals = off zu wählen - doppelt hält besser

04.01.2007 16:54

15 lava



@marcel: Im Buch "Jetzt lerne ich PHP 5" von Matt Zandra steht das Beispiel mit move_uploaded_file drin. Die Funktion copy kannte ich daher noch gar nicht (ich hab das Buch nicht von vorne bis hinten gelesen, sondern springe an die Stellen, in denen ich Antworten zu finden hoffe - manchmal helft ihr mir gewaltig, weil ihr mir zumindest soviel Tips gebt, daß ich wieder weiß, wonach ich überhaupt suchen könnte). Zitat aus dem Buch"Die Funktion move_uploaded_file() übernimmt als Argumente die Quelle und das Ziel und führt die gewünschte Verschiebung durch. Diese Vorgehensweise ist bei hochgeladenen Dateien am sichersten, da sie bestätigt, daß die referenzierte Quelldatei eine hochgeladene Datei ist (und keine Serverdatei, die der restlichen Welt verborgen bleiben sollte)."

@ all : Aber ich hab festgestellt, daß auf dem lokalen Rechner und auch auf dem Hostserver nun die 2. Fehlermeldung verschwindet, wenn ich copy statt move_uploaded_file verwende. Jetzt muß ich also nur noch die Serverrechte setzen lassen (wenn ich den Bekannten mal wieder erreichen kann, bei dem der Server steht, denn das kann ich wohl nicht allein???) bzw. selber setzen (wenn ich wüßte, wie ich das lokal mache ???)

04.01.2007 20:35 | geändert: 04.01.2007 22:09

16 lava

So, jetzt zu Marcels Script:

$key = 0; //$key IST ja wohl eine Spezialvariable (wenn ich auch nicht genau weiß, mit welcher Bedeutung), aber würde es zu diesem Zweck nicht auch eine ganz normale Variable mit beliebigem Namen hier auch tun???

if ($_POST["username"] == "Username" AND $_POST["password"] == "Passwort")
// okay, klingt logisch; aber sind PHP-Dateien denn grundsätzlich nicht über "Quelltext ansehen" einsehbar??? Oder wird unterschieden, ob die Datei ein reines PHP-Script oder eigentlich eine HTML-Datei ist, die halt wegen Verwendung von PHP .php statt .html heißen muß?



{
$key = 1;
$_SESSION["username"] = $_POST["username"];
$_SESSION["password"] = $_POST["password"];
}

if ($_SESSION["username"] == "Username" AND $_SESSION["password"] == "Passwort")
$key = 1;

// also, wenn vorhin die Schleife mit der Password-Abfrage erfolgreich war, ist es ja auch diese Schleife jetzt automatisch-> die Variable _SESSION kannte ich bisher noch nicht, aber ich schlußfolgere mal, daß das eintrifft, wenn das Script wieder aufgerufen wird ohne neues Login (also innerhalb einer "Session", wobei ich das Wort Session bisher nur aus BDSM-Zusammenhängen kenne....)


if ($key == 1) {
echo "<p><b>".$_SESSION["username"]."</b>, erfolgreich eingeloggt</p>";

// aber wenn ich das eben richtig interpretiert habe, dann will ich doch nicht innerhalb einer Session immer wieder schreiben, daß der User erfolgreich eingeloggt ist - oder?


if ($_POST["name"] AND $_FILES["file"]["name"]) {
$name = strip_tags(trim($_POST["name"]));
// über strip_tags hab ich mal was gelesen, über trim noch nicht....
// wenn es mir nur um ein einfaches Upload meiner Dateien auf den Server geht, wo ich die Namen der Dateien ja unproblematisch ohne Sonderzeichen und Tags wählen kann, brauch ich das aber nicht, oder?

if (strlen($name) > 101)
echo "<p><font color=\"red\">Der Name darf maximal 100 Zeichen lang sein.</font></p>";
elseif ($_FILES["file"]["size"] > 512001)
echo "<p><font color=\"red\">Die Datei darf maximal 500 KB gro&szlig; sein.</font></p>";
//okay, begriffen, wenn auch für meinen Zweck unnötig


else {
copy($_FILES["file"]["tmp_name"], "./Bilder/".$name."_".$_FILES["file"]["name"]."");


// so, und hier wird es spannend für mich, um zu verstehen, was denn bei meinem bisherigen Scriptansatz nun (abgesehen von den Rechten, wo ich ja gern noch wissen würde, wie ich mir die zumindest lokal selber setze) falsch war, denn ich hatte ja das Zielverzeichnis noch nicht korrekt angegeben. Leider fragt Marcel in seinem Script das Zielverzeichnis aber gar nicht ab, sondern definiert automatisch /Bilder. Und was ist dieser "_" Teil, hat der was mit der Copy-Funktion zu tun? (Hab in der PHP-Referenz nun Copy nachgeschlagen, da steht aber einfach int copy (string source, string destination) -> also wäre $_FILES["file"]["tmp_name"] die Source (wie bei mir ja auch) und die destination ist irgendwas Zusammengesetztes? Wenn du in dem Script als Namen (Namen wofür?) Christian eingibst und als Datei drachen.jpg, dann würde die Upload-Datei also in also /Bilder/Christian_drache.jpg geladen werden, also in Christian_drache.jpg umbenannt??? Wieso nicht einfach "./Bilder/".$_FILES["file"]["name"]." ?

04.01.2007 21:28 | geändert: 04.01.2007 22:12

17 Marcel (Gast)

Okay, ich versuche dir mein Script mal nach und nach zu erläutern:

Als erstes wird die Variable $key gesetzt (kann auch umbenannt werden, doch finde ich den Namen "key" hier angemessen). Zu dieser Zeit besitzt die Variable den Wert 0. Wenn man die Felder "Username" und "Passwort" ordnungsgemäß und richtig ausgefüllt hat, dann wird in die erste If-Abfrage gesprungen und die Variable $key auf 1 gesetzt. Zudem wird eine Session erstellt, die das erneute Einloggen erübrigt.
Die zweite If-Abfrage wird "geöffnet", wenn eine Session bereits vorhanden ist und die Felder nicht mehr ausgefüllt werden mussten.
Wenn eine der beiden If-Abfragen erfolgreich war, dann besitzt die Variable "key" den int-Wert 1. Dieser Wert bewirkt das erfolgreiche Einloggen in den geschützen Bereich. Ist "key" weiterhin auf 0, wird einem weiterhin das Formular angezeigt.

Die Funktion trim() entfernt das (überflüssige) Leerzeichen am Anfang und am Ende eines Strings (Zeichenkette). Für speziellere Aktionen gibt es noch ltrim() und rtrim(). Mit ltrim() (das l steht für left = links) wird das Leerzeichen am Anfang einer Zeichenkette entfernt. Mit rtrim() kann man das Selbe bezwecken, allerdings auf der anderen Seite des Strings.

copy($_FILES["file"]["tmp_name"], "./Bilder/".$name."_".$_FILES["file"]["name"]."");

Die Datei - die ausgewählte worde - wird in das selbe Verzeichnis in den Ordner "Bilder" kopiert.
Der letzte Teil ist lediglich der Name der Datei. In diesem Fall wäre es der angegebene Name (das erste Feld des Formulars) und der Name der eigentlichen Datei.

04.01.2007 22:22 | geändert: 04.01.2007 22:25

18 lava

Hm, viel schlauer bin ich auch nicht. Du hast fast alles erläutert, was ich bereits wußte und die eigentlichen Fragen sind offengeblieben. Daß es mit der Session so funktioniert, wie du schreibst, hab ich begriffen. Nur wann eine Session "bereits vorhanden sein kann" (also wann wird dieses Script aufgerufen wenn NICHT beim ersten Aufruf des Formulars??) das ist mir unklar.

Wenn du mir trim() erklärst, brauchst du bei ltrim und rtrim eigentlich nichts genaues mehr zu erläutern :) Aber danke.

Jetzt die Sache mit dem Datei-Namen.
./Bilder scheint also das zu sein, was ich mit ../Bilder probiert hatte.
"./Bilder/"./$name ist also klar. Wieso heißt es nicht einfach copy
($_FILES["file"]["tmp_name"], "./Bilder/".$_FILES["file"]["name"]."");
wenn ich die Datei drachen.jpg in das Verzeichnis ./Bilder hochladen will, so daß sie dort als Bilder/drachen.jpg gefunden werden kann?
Was soll der _ ? Und wieso übergibst du im Formular einen Namen? Ich hätte nur ein File (das hat ja einen Filenamen, wie du ja so schön schreibst, nämlich $_FILES["file"]["name"] ) und ein Zielverzeichnis (also das, was du als /Bilder vorgibst - bei mir ist es wichtig, daß ich an verschiedene Verzeichnisse mit dem Script komme....) übergeben. Was soll der zusätzliche Name?

04.01.2007 23:02

19 Marcel (Gast)

Habe ich doch bereits erklärt...
Wenn die Felder ausgefüllt worden und die Daten richtig sind, dann wird $key auf 1 gesetzt und eine Session erstellt. Danach wird noch überprüft ob eine Session vorhanden ist und ob sie richtig gesetzt worde.

Der Name ist einfach nur eine Spielerei. Du könntest die Datei auch nur mit dem Namen der Datei speichern. Natürlich kannst du dieses Feld entfernen.

04.01.2007 23:20

20 Jörg Kruse

Was soll der _ ? Und wieso übergibst du im Formular einen Namen? Ich hätte nur ein File (das hat ja einen Filenamen, wie du ja so schön schreibst, nämlich $_FILES["file"]["name"] ) und ein Zielverzeichnis (also das, was du als /Bilder vorgibst - bei mir ist es wichtig, daß ich an verschiedene Verzeichnisse mit dem Script komme....) übergeben. Was soll der zusätzliche Name?

$_FILES ist eine Autoglobale für den Dateiupload sowie $_GET für die Übergabe von Variablen über die URL - deswegen sind beide gleichermaßen durch den _ gekennzeichnet. Die verschiedenen Inhalte von $_FILES sind hier erläutert:

http://de3.php.net/manual/de/features.file-upload.php#features.file-upload.post-method

'file' entspricht in diesem Fall dem Namen des Uploads, der im Formular angegeben wurde:

 echo "<td><b>Datei</b>:</td><td><input type=\"file\" name=\"file\"></td>";

04.01.2007 23:30 | geändert: 04.01.2007 23:31