Manu3l: Datei-Upload auf Apache2 Server

Hallo,

bin eigentlich schon länger hier auf der Seite unterwegs, weil es immer wieder nützliche Tipps hier gibt. Aber nun bin ich irgendwie wohl auf dem falschen Weg und hab mich mal fürs Forum registriert. :)

Ich bräuchte Hilfe, um auf meinem Linuxserver (Apache2-Server) einen Datei-Upload einzubinden, überwiegend für Bilder.

Ich habe im Rootverzeichnis des Apacheservers (www/html) einen Ordner "uploads" angelegt. In diesen Ordner habe ich einmal eine "index.html" und eine "upload.php"-Datei angelegt und einen "upload"-Ordner, in dem die Hochgeladenen Dateien gespeichert werden sollen.

In meiner index.html Datei habe ich folgendes Script:

	<!DOCTYPE html>
	<html lang="de">
	<head>
	<meta charset="utf-8"/>
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<title>Upload</title>
	</head>
	<body>
	
	<form action="upload.php" method="POST" enctype="multipart/form-data">
	<input type="file" name="file">
	<button type="submit" name="submit">UPLOAD</button>
	</form>
	
	</body>
	</html>

In meiner upload.php habe ich folgendes Script:

	<?php  
	if (isset($_POST['submit'])) {
	$file = $_FILES['file'];

	$fileName = $_FILES['file']['name'];
	$fileTmpName = $_FILES['file']['tmp_name'];
	$fileSize = $_FILES['file']['size'];
	$fileError = $_FILES['file']['error'];
	$fileType = $_FILES['file']['type'];

	$fileExt = explode('.', $fileName);
	$fileActualExt = strtolower(end($fileExt));

	$allowed = array('jpg', 'jpeg', 'png', 'pdf');

	if (in_array($fileActualExt, $allowed)) {
		if ($fileError === 0) {
			if ($fileSize < 1000000) {
			$fileNameNew = uniqid('', true).".".$fileActualExt;
			$fileDestination = 'uploads/'.$fileNameNew;
			move_uploaded_file($fileTmpName, $fileDestination);
			header("Location: index.php?uploadsuccess");
		} else {
			echo "Your file is too big!";		
		   }
		} else {
			echo "There was an error uploading your file!";
       		   }
	        } else {
		        echo "You cannot upload files of this type!";
	}
}

Mir ist bewusst, dass noch zwei "else" fehlen, hatte ich gestern Abend noch angepasst, aber leider kein Zugriff im Moment drauf, aber dies sollte nicht zu meinem Problem führen.

Wenn ich nun z.B. eine test.jpg Datei hochlade, zeigt mir der Browser (Chrome) zwar an, dass er die Datei hochläd, jedoch befindet sich nichts im vom mir angegebenen "upload"-Ordner. Auch unter var/tmp befindet sich diese Datei nicht. Auch mit find / test.jpg finde ich die Datei nicht.

Hier auf selfhtml Datei-Upload steht dazu folgendes:

Beachten Sie: Beim Absenden des Formulars wird zwar die gewählte Datei auf den Server übertragen, jedoch nur in einem temporären Verzeichnis gespeichert. Beim Aufruf einer weiteren URL durch den Browser wird sie wieder verworfen. Es bedarf serverseitig eines Scriptes (PHP, Perl, JSP …), um die Datei nach dem Upload dauerhaft in ein Verzeichnis auf dem Server abzulegen.

Mir ist bewusst, dass ich nach erfolgreichem Hochladen auf die Seite "index.php?uploadsuccess" verweise und somit eine neue URL aufrufe, aber auch, wenn ich diese Zeile weg lasse, finde ich die "test.jpg" Datei nicht.

Entweder habe ich einen echt dummen Denkfehler, oder irgendwas stimmt mit meinem Script nicht. Wie kann ich meine Testdatei auf dem Server finden?

War jetzt doch recht viele geschrieben, aber ich hoffe ihr könnt mir weiterhelfen. Danke schonmals!

Manuel

  1. Tach!

    Hier auf selfhtml Datei-Upload steht dazu folgendes:

    Beachten Sie: Beim Absenden des Formulars wird zwar die gewählte Datei auf den Server übertragen, jedoch nur in einem temporären Verzeichnis gespeichert. Beim Aufruf einer weiteren URL durch den Browser wird sie wieder verworfen. Es bedarf serverseitig eines Scriptes (PHP, Perl, JSP …), um die Datei nach dem Upload dauerhaft in ein Verzeichnis auf dem Server abzulegen.

    Richtig wäre, am Ende des Requests wird gelöscht, nicht beim nächsten. (Korrigiert)

    Entweder habe ich einen echt dummen Denkfehler, oder irgendwas stimmt mit meinem Script nicht. Wie kann ich meine Testdatei auf dem Server finden?

    Erstmal Debugging betreiben. Was steht in den Variablen drin? Verzweigt das Programm richtig? Kontrollausgaben schaffen Gewissheit.

    Übrigens, das Umkopieren des Inhalts von $_FILES in andere Variablen ist nicht notwendig und macht nur zusätzliche Arbeit, besonders wenn die Variablen dann nur einmal verwendet werden.

    dedlfix.

    1. Bin jetzt alles durchgegangen und habe die Scripte "vereinfacht". Versuche es heute Abend nocheinmal. Aber Danke für den Hinweis mit der Variable $_FILES. Hatte ich komplett überlesen.

  2. Hello Manuel,

    wie suchst Du denn nach dem Upload nach der Datei? Mit dem Browser beim Webserver, oder auf dem Server mit Dateilisting-Funktionen?

    Die Pfade in $_FILES und für move_uploaded_file() (nicht mehr empfehlenswert!) beziehen sich auf das Dateisystem, nicht auf die DocumentRoot des Webhosts.

    Bitte lies dir auch auf jeden Fall die Gedanken im PHP-Artikel zum Dateiupload durch. Es geht um deine Serversicherheit!

    Glück Auf
    Tom vom Berg

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.
    1. Hab die Datei über Filezilla und SSH (Putty) gesucht. Wie geschrieben, über Putty mit dem "find" Befehl.

      Du schreibst, dass move_uploaded_file() nicht mehr empfehlenswert ist, was wäre denn die Alternative?

      Ich bin aber auf der Suche nach einer Alternative auf einen Fehler von jemandem gestoßen, den ich glaube ich auch gemacht hab. Wenn ich den Absoluten Pfad angebe (anstatt 'uploads/) lautet dieser '/var/www/html/image/...' Ich hatte immer ohne / vor var beim testen angegeben. Evtl. muss es auch '/Uploads/' heißen? (Aber eigentlich ja nicht?!?)

      Die Sicherheit hatte ich durchgelesen, daher hatte ich extra die Überprüfungen von Namen, Typ usw. in das Script geschrieben. Aber die Seite ist auch nicht öffentlich und nur ein paar Freunde können auf die Seite zugreifen.

      Muss aber ehrlich sagen, dass ich das Script erst einmal zum laufen bringen möchte, bevor ich dann an die Sicherheit gehe. Damit ich da Fehler ausschließen kann.

      1. Hello,

        Du schreibst, dass move_uploaded_file() nicht mehr empfehlenswert ist, was wäre denn die Alternative?

        Das steht in dem verlinkten Artikel genau beschrieben.

        Glück Auf
        Tom vom Berg

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
      2. Hallo

        Du schreibst, dass move_uploaded_file() nicht mehr empfehlenswert ist, was wäre denn die Alternative?

        Wäre schön, wenn @TS erst einmal schriebe, warum er move_uploaded_file nicht mehr für empfehlenswert hält. Ich finde dazu [edit]im Netz[/edit] nichts und auch der sehr umfangreiche und dennoch hier und da lückenhafte Artikel enthüllt mir dieses Geheimnis beim querlesen nicht.

        [edit]Mittlerweile habe ich den Abschnitt im Artikel gefunden. Ob die postulierten Probleme tatsächlich auftreten, kommt auf den Einzelfall (Konfiguration des Servers) an. Es können im Einzelfall keine, einzelne oder alle der genannten Probleme auftreten. Dennpoch kann die von TS geschmähte Funktion schlussendlich benutzt werden, um den Upload zu speichern.[/edit]

        Wenn ich den Absoluten Pfad angebe (anstatt 'uploads/) lautet dieser '/var/www/html/image/...' Ich hatte immer ohne / vor var beim testen angegeben. Evtl. muss es auch '/Uploads/' heißen? (Aber eigentlich ja nicht?!?)

        Du weißt offensichtlich noch nicht so recht, wie Pfadangaben in unixoiden Systemen funktionieren. Ein Pfad, der mit einem Slash beginnt (z.B. /var/www/html/), ist absolut angegeben. Im Dateisystem heißt das, dass es im Wurzelverzeichnis, auf der obersten Ebene des Dateisystems, ein Verzeichnis var gibt, das wiederum das Verzeichnis www mit dem Unterverzeichnis html enthält.

        Daneben gibt es relative Pfadangaben, die, vom gegenwärtigen Standort ausgehend (z.B. auszuführendes Skript, in der Konsole geöffnetes Verzeichnis), notiert werden.

        • Datei im gegenwärtigen Verzeichnis: „index.html“ oder „./index.html“
        • Unterverzeichnis: „verzeichnis“ oder „verzeichnis/“
        • Datei in einem Unterverzeichnis: „verzeichnis/index.html“
        • übergeordnetes Verzeichnis: „../“ (bei Bedarf auch mehrfach notiert („../../../“ für drei Ebenen))
        • Datei im übergeordneten Verzeichnis: „../index.html“

        Nun mag es auf der absolut obersten Ebene auch ein Verzeichnis Uploads geben, aber höchstwahrscheinlich meinst du ein gleichnamiges Verzeichnis innerhalb des Webauftritts. Dann sollte es aber auch entweder mit dem korrekten absoluten Pfad oder mit einem relativen Pfad, ausgehend vom laufenden Skript angesprochen werden.

        Tip: PHP kennt die Variable $_SERVER['DOCUMENT_ROOT'], mit dem man das Wurzelverzeichnis des Webauftritts innerhalb des Dateisystems des Servers ermitteln kann. Das ist als Ausgangspunkt von projektinternen Pfadangaben hilfreich.

        Muss aber ehrlich sagen, dass ich das Script erst einmal zum laufen bringen möchte, bevor ich dann an die Sicherheit gehe. Damit ich da Fehler ausschließen kann.

        Allzuoft (sprich: fast immer) heißt, es später zu tun, es nie zu tun. Also tu es gleich.

        Tschö, Auge

        --
        Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
        Hohle Köpfe von Terry Pratchett
  3. Lieber Manu3l,

    auf meinem Linuxserver (Apache2-Server) [...]
    einen Ordner "uploads" angelegt.

    und welche Dateiberechtigungen hast Du vergeben bzw. im Einsatz? Erzähle doch bitte mehr darüber. Insbesondere empfehle ich das Einrichten einer Benutzergruppe (nennen wir sie einfach mal web), welcher sowohl Dein Benutzer, als auch der Apache-Prozess (www-run oder www-data) angehören, damit Du und der Apache mit PHP dort lesen und schreiben können. Die Verzeichnisse bekommen chmod 0770, die Dateien chmod 0660. Besitzer ist eigentlich egal, wenn alles die passende Gruppe hat, trotzdem darf Dein Benutzer der Besitzer sein.

    Liebe Grüße

    Felix Riesterer

    1. Hello,

      wer etwas tiefer in die Möglichkeiten der Apache-Benutzerverwaltung einsteigen mag, beachte die Direktive VHostUser

      Hiermit kann jedem VirtHost (Domain) ein eigener User zugewiesen werden, unter dessen Namen dann die Scripte ausgeführt werden.

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Tach!

        wer etwas tiefer in die Möglichkeiten der Apache-Benutzerverwaltung einsteigen mag, beachte die Direktive VHostUser

        Hiermit kann jedem VirtHost (Domain) ein eigener User zugewiesen werden, unter dessen Namen dann die Scripte ausgeführt werden.

        Wenn der Apache auf Solaris läuft. Tut er selten.

        dedlfix.

        1. Hello Dedlfix,

          wer etwas tiefer in die Möglichkeiten der Apache-Benutzerverwaltung einsteigen mag, beachte die Direktive VHostUser

          Hiermit kann jedem VirtHost (Domain) ein eigener User zugewiesen werden, unter dessen Namen dann die Scripte ausgeführt werden.

          Wenn der Apache auf Solaris läuft. Tut er selten.

          Das ist mir jetzt neu. Davon war ursprünglich nicht die Rede. Muss ich nachher nochmal testen! Das sollte ursprünglich ab Apache 2.4 auf jeder Plattform funktionieren.

          Glück Auf
          Tom vom Berg

          --
          Es gibt nichts Gutes, außer man tut es!
          Das Leben selbst ist der Sinn.
          1. Tach!

            Wenn der Apache auf Solaris läuft. Tut er selten.

            Das ist mir jetzt neu. Davon war ursprünglich nicht die Rede. Muss ich nachher nochmal testen! Das sollte ursprünglich ab Apache 2.4 auf jeder Plattform funktionieren.

            Das steht jedenfalls unter Compatibility auf der verlinketen Seite. Mir ist ein Nutzerwechsel nur im Zusammenhang mit Suexec bekannt, also für CGI-Scripte. Ein Nutzer kann meines Wissens nur für den Prozess gesetzt werden und das hieße, dass für jeden Request ein Prozess gestartet werden muss oder zumindest für den VHost ein separater.

            dedlfix.

    2. Hallo Felix,

      danke für den Hinweis! Muss ich mich noch genauer damit beschäftigen.

      Zur Zeit läuft der Apacheserver über die rootrechte des Servers. Für die einzelnen Server (Teamspeak, Minecraft, ...) habe ich extra User mit Passwort eingerichtet.

      Grundsätzlich muss ich auch dazu sagen, auf dem Virtuellen Server habe ich keinerlei sensible Daten. Ich habe auch nichts in dieser Richtung gelernt, arbeite in einem ganz anderen Bereich. Für mich ist das nur ein Hobby, mich abends an irgendwelche Scripte, wie einen Browserbasierenden Pizzatimer der bei einer manuell einzugebenden Minutenanzahl eine Nachricht an mein Handy versendet, Serverwartungen über Browser usw. zu wagen. (ja, es gibt Timer auf dem Handy, ist aber langweilig ;) )

      Da ich am Wochenende mit Freunden auf einem Konzert in München war, habe ich mir vorgestellt, dass jeder über die Seite die Bilder hochladen können sollte, damit ich Sie gesammelt wieder verteilen bzw. bereitstellen kann. (ja, es gibt Seiten, die dies auch tun, aber wie oben, das wäre langweilig ;) )

      Aber wenn ich es lerne, möchte ich natürlich auch Dinge über die Sicherheit lernen!

      Manuel

      1. Tach!

        Zur Zeit läuft der Apacheserver über die rootrechte des Servers.

        Sicher? Hast du kein User und Group konfiguriert, beziehungsweise das deaktiviert, was da üblicherweise gesetzt wird? Der Apache startet eigentlich als root, beantwortet die Requests aber als der dort angegebene User.

        dedlfix.