Jun 09
PHP Uploadscript Tutorial
Da ich mich in letzter Zeit ein bisschen mit diesem Thema befasst habe, habe ich ein kleines Tutorial zum Thema PHP Uploadscript geschrieben. Ziel ist es, Dateien aus dem Browser direkt in ein Verzeichnis zu laden. Auch sollen die hochzuladenden Dateitypen in unserem Uploadscript beschränkbar sein da dies eine gravierende Sicherheitslücke wäre.
Uploadscript Tutorial Vorraussetzungen
- mittlere PHP Kenntnisse
- Schwierigkeit: Mittel
Uploadscript Sicherheit
Man stelle sich nun vor es wäre jedem erlaubt jeden beliebigen Dateityp auf den Server zu laden. So wäre es einfach mittels einer hochgeladenen .php Datei einen Angriff auf den Server zu starten. Deshalb sollte vorgegeben werden welche Dateitypen erlaubt sind, und welche nicht.
Ein weiteres Sicherheitsrisiko sind die Servereinstellungen. Leider gibt es im World Wide Web immer noch schwarze Schafe unter den Webhostern, welche einen unprofessionellen und völlig falsch konfigurierten Server bzw. Webspace anbieten. Es ist also möglich das Dateien wie bild.php.jpg als php Datei ausgeführt werden. Dies sollte vor Installation eines Uploadscriptes unbedingt beachtet werden (Bevor du das Tutorial also weiterliest, prüfe bitte ob dies bei dir der Fall ist).
Das Uploadscript
Ich werde als erstes wieder das komplette Uploadscript posten, und dies dann im Laufe des Tutorials durcharbeiten.
<?php $max_size = 2; // In MB $max_size = $max_size*pow(10,6); $upload_pfad = "uploads/"; $nachricht = array(); function check_file($dateiname, $pfad, $error, $mimetyp, $max_size) { $error = false; /* Fehlermeldungen definieren */ switch($error) { case 1; $error[] = "Die hochgeladene Datei überschreitet die maximal festgelegte Größe."; break; case 2; $error[] = "Die hochgeladene Datei überschreitet die maximal festgelegte Größe."; break; case 3; $error[] = "Die Datei wurde nur teilweise hochgeladen, versuchen sie es erneut"; break; case 4; $error[] = "Es wurde keine Datei hochgeladen."; break; } /* Nochmals Größe der Datei prüfen */ if(filesize($_FILES["dateiupload"]["tmp_name"])>$max_size) $error[] = "Die hochgeladene Datei überschreitet die maximal festgelegte Größe."; /* Dateityp prüfen */ $mime_erlaubt = array("image/gif", "image/jpeg", "image/png", "text/html", "text/plain", "text/css", "application/zip", "video/mpeg"); if(!in_array($mimetyp, $mime_erlaubt)) $error[] = "Unerlaubter Dateityp"; /* Prüfen ob Datei schon existiert, wenn Ja, die datei umbennenen */ while(file_exists($pfad.$dateiname)) { $dateiname = "a_".$dateiname; } $_FILES["dateiupload"]["name"] = $dateiname; /* Fehlermeldungen zurückgeben, wenn keine Fehler wird false zurückgegeben */ return $error; } /* Datei hochladen */ if(isset($_GET['action']) and ($_GET['action']=="upload")) { $nachricht = check_file($_FILES["dateiupload"]["name"], $upload_pfad, $_FILES["dateiupload"]["error"], $_FILES["dateiupload"]["type"], $max_size); if(!$nachricht) { /* File nun in das richtige Verzeichnis verschieben */ if(move_uploaded_file($_FILES["dateiupload"]["tmp_name"], $upload_pfad.$_FILES["dateiupload"]["name"])) { if(!chmod($upload_pfad.$_FILES["dateiupload"]["name"], 0755)) $nachricht[] = "Rechte der Datei wurden nicht gesetzt, dies ist möglicherweise eine Sicherheitslücke"; $nachricht[] = "Datei wurde erfolgreich hochgeladen"; } else $nachricht[] = "Unbekannter Fehler! Bitte versuchen sie es erneut"; } } ?>
<form enctype="multipart/form-data" action="?action=upload" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $max_size ?>"> Datei hochladen: <input name="dateiupload" type="file"> <input type="submit" value="Upload"> </form>
<?php /* Fehlermeldung ausgeben */ foreach($nachricht as $msg) { echo $msg."<br>"; } ?>
So nun schauen wir uns das ganze etwas näher an, als erstes den HTML Teil.
<form enctype="multipart/form-data" action="?action=upload" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $max_size ?>"> Datei hochladen: <input name="dateiupload" type="file"> <input type="submit" value="Upload"> </form>
Durch dieses Formular können mit jedem RFC-1867 konformen Browser (dazu gehören der Netscape Navigator 3 oder höher, Microsoft Internet Explorer 3 mit einem Patch von Microsoft oder höher ohne Patch) Dateiuploads durchgeführt werden. Mittels PHP kann man dann steuern wer Dateien hochladen darf und was mit diesen Dateien geschieht. Sobald dieses Formular abgeschickt wurde, wird eine Datei in ein temporäres Verzeichnis geuploaded. Mit MAX_FILE_SIZE kann erstmal einfach eine maximale Dateigröße vorgegeben werden (Welche in diesem Tutorial in der Variable $max_size steht). Achtung: Diese Prüfung kann sehr einfach umgangen werden!
Kommen wir zum nächsten Teil des PHP Uploadscript Tutorials, der Funktion check_file() welche den MIME-Typ, die Dateigröße und änhliches prüft. Als ersten Teil der Funktion schauen wir uns die Variable $_SERVER["dateiupload"]["error"] an, in welcher, falls es zu einem Uploadfehler kommt, ein Fehlercode steht. (1, 2, 3 oder 4).
/* Fehlermeldungen definieren */ switch($error) { case 1; $error[] = "Die hochgeladene Datei überschreitet die maximal festgelegte Größe."; break; case 2; $error[] = "Die hochgeladene Datei überschreitet die maximal festgelegte Größe."; break; case 3; $error[] = "Die Datei wurde nur teilweise hochgeladen, versuchen sie es erneut"; break; case 4; $error[] = "Es wurde keine Datei hochgeladen."; break; }
In diesem Abschnitt des Uploadscriptes wird also mittels Switch Abfrage geprüft welcher Fehler vorkam, und gegebenenfalls eine passende Fehlermeldung in das Array $error, welches am Ende der Funktion zurückgegeben wird, geschrieben.
Im nächsten Teil der Funktion check_file(), das nochmalige prüfen der Dateigröße, da wie oben schon beschrieben die Vorgabe mittels MAX_FILE_SIZE unsicher ist.
/* Nochmals Größe der Datei prüfen */ if(filesize($_FILES["dateiupload"]["tmp_name"])>$max_size) $error[] = "Die hochgeladene Datei überschreitet die maximal festgelegte Größe.";
Es wird also die Dateigröße der bereits hochgeladene Datei (temporär gespeichert) überprüft, und falls diese Größe zu groß ist, wird eine Fehlermeldung in unserer Array $error geschrieben. Im nächsten Teil von check_file() wird der MIME-Typ geprüft, denn es werden nur einige Vorgegebene MIME-Typen in dem Uploadscript zugelassen.
/* Dateityp prüfen */ $mime_erlaubt = array("image/gif", "image/jpeg", "image/png", "text/html", "text/plain", "text/css", "application/zip", "video/mpeg"); if(!in_array($mimetyp, $mime_erlaubt)) $error[] = "Unerlaubter Dateityp";
Hier erstellen wir einen Array mit dem Namen $mime_erlaubt, in welchen alle erlaubten MIME Typen stehen. Mittels einer If Abfrage und der Funktion in_array prüfen wir nun ob der MIME Typ der hochgeladenen Datei ein erlaubter MIME Typ ist. (Den MIME Typ der hochgeladenen Datei bekommt man mittels $_FILES["dateiupload"]["type"]).
Im vorletzten Abschnitt unserer eigenen Funktion check_file() überprüfen wir ob eine Datei schon existiert, und geben der Datei gegebenenfalls einen Präfix...
/* Prüfen ob Datei schon existiert, wenn Ja, die datei umbennenen */ while(file_exists($pfad.$dateiname)) { $dateiname = "a_".$dateiname; } $_FILES["dateiupload"]["name"] = $dateiname;
Es wird also mittels While-Schleife und der Funktion file_exists geprüft ob die Datei mit dem Dateinamen $dateinamen im Verzeichnis $pfad vorhanden ist, und solange dies der Fall ist wird dem Dateinamen ein "a_" angehängt. Abschließend wird unserer Superglobalen $_FILES["dateiupload"]["name"] welchen den Namen der Datei beinhaltet, der neue Dateiname zugewiesen.
Als Abschluss der Funktion check_file() geben wir jetzt nur noch den Array mit den Fehlermeldungen zurück.
Kommen wir also zum nächsten Schritt des Tutorials, dem aufrufen der Funktion und dem Verschieben der temporär gespeicherten Datei.
/* Rufen wir also die selbt definierte Funktion auf Und übergeben den Dateinamen, den Pfad, ggf den Fehler der beim Hochladen verursacht wurde Den MIME-Typ und die maximale Filegröße */ $nachricht = check_file($_FILES["dateiupload"]["name"], $upload_pfad, $_FILES["dateiupload"]["error"], $_FILES["dateiupload"]["type"], $max_size); /* Da wir die Fehlermeldungen in der Funktion zurückgeben enthält nun die Variable $nachricht Fehlermeldungen, wenn welche vorhanden sein sollten. Deshalb prüfen wir im nächsten Schritt ob diese Variable false, d.h nicht gesetzt ist */ if(!$nachricht) { /* Mittels move_uploaded_file verschieben wir die temporär gespeicherte Datei in unser Wunschverzeichnis */ if(move_uploaded_file($_FILES["dateiupload"]["tmp_name"], $upload_pfad.$_FILES["dateiupload"]["name"])) { /* Und als Abschluss noch chmod Rechte setzen damit nicht jeder mit den Dateien machen kann was er will */ if(!chmod($upload_pfad.$_FILES["dateiupload"]["name"], 0755)) $nachricht[] = "Rechte der Datei wurden nicht gesetzt, dies ist möglicherweise eine Sicherheitslücke"; $nachricht[] = "Datei wurde erfolgreich hochgeladen"; } else $nachricht[] = "Unbekannter Fehler! Bitte versuchen sie es erneut"; } }
Und um das Tutorial noch abzuschließen werden die Meldungen aus dem Array $nachricht noch mittels einer Foreach-Schleife ausgegeben.
<?php /* Fehlermeldung ausgeben */ foreach($nachricht as $msg) { echo $msg."<br>"; } ?>
Ich hoffe ich konnte euch mit diesem PHP Uploadscript Tutorial etwas helfen und wäre über Rückmeldungen, Fragen oder Kommentare jeder Art dankbar.

August 31st, 2008 at 6:25 pm
Hallo, erst mal Hut ab, nach langen stöbern bei Google bin ich auf Dein Srcipt gestossen, ist mit Abstand das beste das ich finden konnte. Habe aber noch eine Frage: Ich versuche damit auch mp3 Dateien hochzuladen und hab unter den mime typen folgendes eingetragen: “audio/x-mp3″,”audio/mp3″, bekomme aber trotzdem als Fehlermeldung: Unerlaubter Dateityp.
Evtl. weißt Du eine Lösung, oder muß ich den Mime typ noch anderweitig erlauben?
Gruß
Edgar
August 31st, 2008 at 11:01 pm
Hallo!
Aus folgender Liste kannst du Diverse Mimetypen entnehmen: http://de.selfhtml.org/diverses/mimetypen.htm
Leider finde ich dort auch nirgends etwas in Richtung .mp3! Was du machen kannst ist vor dem Prüfen der Datei “echo $_FILES["dateiupload"]["type"];” einzufügen, und dann eine Datei hochladen! Dann wird dir der Mime-Typ ausgegeben.
mfg Leichti