Wie kann ich ein Upload-Skript erstellen?

(Autor: Darkmage & perl-community)

Bevor ich auf den eigentlichen Mechanismus eingehe, hier einige Anmerkungen zur Uebertragung von Dateien per Browser:

  • Dieser Punkt ist relevant, wenn das Script unter Windows laufen soll: Man muss wissen, von welchem Typ eine Datei ist. Hierbei Unterscheidet man grundsaetzlich zwischen ASCII (Text) und Binary (Ausfuehrbare Programme oder auch Bilder, Zip-Archive, etc.). Waehrend das Speichern einer ASCII-Datei im Binary-Modus meist problemlos ist, so ist das Speichern von Binaries im ASCII-Modus verheerend. (Voellige Unbrauchbarkeit des Programms wegen zerstoertem Code)

  • Wer das Uploaden von Dateien erlaubt, geht grundsaetzlich Risiken ein. Binaries koennen mit einem Virus oder einem Trojaner infiziert sein. Man sollte sich dieser Gefahr bewusst sein und eventuell einige Restriktionen einbauen (Kein Ausfuehren von auf dem Server liegende Dateien, Beschraenkung auf ASCII Dateien). Man kann auch die Endung des Dateinamens überprüfen, auch wenn das keine absolute Sicherheit bringt, da man mit Dateien beliebiger Endungen schlimme Sachen machen kann. Insbesondere unter Unix können Dateien unabhängig von der Endung ausgeführt werden.


Nun gehen wir an die Erstellung eines Upload-Scripts. Doch vor das Programm haben die CGI-Goetter das Formular gesetzt. Dieses kann entweder als einfache Datei angeboten werden oder direkt im Script eingebaut sein. So oder so, bei der Erstellung muessen zwei Dinge beachtet werden:

  • In der HTML-Sprachdefinition gibt es einen sogenannten Upload-Button. Dieser besteht aus einem Textfeld und einem Auswahlbutton.

                <INPUT TYPE="file" NAME="uploaded_file" SIZE=30 MAXLENGTH=80>
                

TYPE

Der Typ des Eingabefeldes. Fuer ein Dateiauswahlfeld ist file zwingend.

NAME

Beliebig, muss jedoch logischerweise gleichnamig zu den in dem Script verwendeten Namen sein.

SIZE

Groesse des Feldes im Browser

MAXLENGTH

Maximale Groesse der Datei in KByte. In unserem Beispiel wird die Groesse der Datei auf 80 KByte reduziert. Diese Methode funktioniert aber bei weitem nicht immer und ein Cracker kann immer noch mit einem modifizierten Formular arbeiten oder sogar auf einen eigenen WWW Client zurueckgreifen. Daher rate ich dringend dazu, die Kontrollen bezueglich Dateiart und Groesse durch den Server vornehmen zu lassen.

  • Die Formulardaten muessen mit dem Typ multipart/form-data encodiert werden. Dies uebernimmt der Browser, man muss es ihm jedoch explizit mitteilen. Wird dies nicht gemacht, erhaelt das Script keine verwertbaren Daten.


                <FORM METHOD="POST" ACTION="upload.pl" ENCTYPE="multipart/form-data">
                


METHOD

Die Sendemethode POST ist bei Dateiuploadvorgaengen zwingend.

ACTION

Der Name und evtl der Pfad zum Uploadscript. Wird das Formular vom Script selbst erzeugt, so ist hier die Funktion url() von CGI ungemein nuetzlich.

ENCTYPE

Fordert den Browser auf, die Daten im codierten Modus zu senden.

Hier ein kleines Beispielformular:

                <HTML>
                <HEAD></HEAD>
                <BODY>
                        <FORM METHOD="POST" ACTION="upload.pl" ENCTYPE="multipart/form-data">
                        Datei auswaehlen:<BR>
                        <INPUT TYPE="file" NAME="uploaded_file" SIZE=30 MAXLENGTH=80>
                        <INPUT TYPE="submit" NAME="button" VALUE="upload">
                        </FORM>
                </BODY>
                </HTML>
                </pre>%lt;/code>
                



Nun kommt das eigentlich Script:

                use CGI qw/:standard/;
                my $zielVerzeichnis = '/foo/bar';
                $CGI::POST_MAX=1024 * 100;
                



Hier wird folgendes gemacht

  • use CGI qw/:standard/;
    Importiert die wichtigsten Funktionen aus dem Modul CGI. Dies ist essentiell um die hochgeladene Datei zu verarbeiten.

  • my $zielVerzeichnis = '/foo/bar';
    In dieser Variable wird festgelegt, wohin Ihr Script die hochgeladene Datei speichert. Handelt es sich um ein Unixsystem, so sehen die Pfade meist aus wie im oben genannten Beispiel. Unter Windows-Systemen muesste es 'c:/foo/bar' heissen. Wichtig hierbei ist ein kompletter absoluter Pfad (vom Basisverzeichnis aus), da sich der Ausfuehrungspfad des CGI drastisch von seinem wirklichen Verzeichnis unterscheiden kann.

  • $CGI::POST_MAX=1024 * 100;
    Hier wird eine der globalen Variablen genutzt, die das CGI-Modul bietet. Dies unterbindet ein Posting, welches groesser als 100 Kilobyte ist. Wuenschen Sie einen hoeheren Wert, so aendern Sie es entsprechend ab oder loeschen Sie es, wenn Sie keinerlei Beschraenkung wuenschen.

    ACHTUNG: Um eine "Denial of Service"-Attacke zu vermeiden sollte dieser Wert gesetzt sein. Sonst koennte ein User mit dunklen Motiven den Server mit einer Datei von mehreren Gigabyte leicht zum Absturz bringen. Beachten Sie zudem, das es sich hier um die Groesse des POSTINGS handelt und nicht um die Groesse der Datei. Zu dem Posting gehoeren einige grundlegende Informationen, die der Browser mitsendet, sowie ALLE Eingabefelder und deren Inhalt. Weil der Versuch, ein Posting mit mehr als $POST_MAX Bytes durchzufuehren, zu einem fatalen Fehler fuehrt, koennen Sie CGI::Carp verwenden, um den fatalen Fehler im Browser-Fenster auszugeben.

                my ($filename) = param('uploaded_file') || "";
                

  • Setzt die Variable $filename auf den Formularwert des Feldes uploaded_file oder, wenn leer, auf "".

                print header, start_html;
                print ("Filename: $filename<BR><HR>\n");
                

  • Gibt den Dateinamen im Browser aus.

                my $serverFile = $zielVerzeichnis . "/" .
                (split(/[\\\/]/, param('uploaded_file')))[-1];
                

  • Erzeugt eine Variable $serverFile und speichert darin den Pfad der Datei auf dem Server. Hierbei wird der Inhalt von uploaded_file auf den eigentlichen Dateinamen reduziert.
    Dies ist ungemein wichtig, da uploaded_file den Dateinamen mitsamt dem Pfad auf dem Clientrechner enthaelt. z.B. e:\Eigene Dateien\Datei.exe, welcher auf 'unserem' Server ja meist nicht existiert.

                if (-e $serverFile){
                        print ("File is already existing\n");
                }
                

  • Wenn die Datei bereits auf dem Server existiert, erscheint eine Fehlermeldung. Zu der Bedeutung von -e lesen Sie perlfunc.

  • Nun der eigentliche Schreibvorgang:

                else {
                        print ("Writing to file $serverFile<BR>\n");
                        my $buffer;
                        open (FILE,">$serverFile") or die $!;
                        binmode(FILE); # Nur relevant fuer Windows-Rechner

                        while (my $bytesread=read($filename,$buffer,1024)) {
                                print FILE $buffer;
                                }
                                close (FILE);
                        }

                        print end_html;
                

  • Hier wird die Datei im Binarymodus geschrieben. Da wie bereits erwaehnt ein Dateiupload von ASCII-Dateien im Binaermodus eigentlich ungefaehrlich ist, habe ich mich hier auf diese Variante beschraenkt. Die Angabe binmode ist nur fuer Windows-Server relevant, bei diesen aber zwingend!

  • Handelt es sich um einen Unix/Linuxserver sollten Sie zusaetzlich noch ein

                    chmod (0666, "$serverFile");
                    

    anhaengen damit die Datei die benoetigten Rechte auf dem Server hat. aendern Sie die Rechte ggf. entsprechend der Anforderungen ab.

Hier das Script im ganzen:

                #!/usr/bin/perl

                use CGI qw/:standard/;
                my $zielVerzeichnis = 'c:/upload';
                $CGI::POST_MAX=1024 * 100;

                my ($filename) = param('uploaded_file') || "";
                my ($filehandle) = CGI::upload('uploaded_file');

                print header, start_html;
                print ("Filename: $filename<BR><HR>\n");

                my $serverFile = $zielVerzeichnis . "/" .
                (split(/[\\\/]/, param('uploaded_file')))[-1];


                if (-e $serverFile){
                        print ("File is already existing\n");
                        }

                else {
                        print ("Writing to file $serverFile<BR>\n");

                        my $buffer;
                        open (FILE,">$serverFile") or die $!;
                        binmode $filehandle;
                        binmode(FILE); # Nur relevant fuer Windows-Rechner
                        while (my $bytesread=read($filehandle,$buffer,1024)) {
                                print FILE $buffer;
                                }
                        close (FILE);
                        }

                chmod (0666, "$serverFile");

                print end_html;

                



Noch eine wichtige Information

Bei bestimmten Serverkonfigurationen ist die Laufzeit eines CGIs beschraenkt, sprich, es bricht nach einer bestimmten Laufzeit ab. Dies kann ein solches Uploadscript gefaehrden, da der Uploadprozess und die Dauer dessen von der Groesse der Datei, der Netzanbindung des Clients und der Netzanbindung (und der Auslastung) des Servers abhaengt. aendern Sie daher die Werte in Ihrer Serverkonfiguration entsprechend ab, wenn es zu Problemen dieser Art kommt.

Das Script an sich ist eine leicht geaenderte (und groesstenteils gekuerzte wink Version von Martin Fabianis Script.

Siehe auch:

perldoc CGI (CGI-Manpage)
http://www.oreilly.de/catalog/perlmodger/manpage/cgi.htm (Deutsche uebersetzung)
http://www.linux-magazin.de/ausgabe/1998/03/CGI/cgi1.html (Interessante Artikel zu CGI)
http://www.linux-magazin.de/ausgabe/1998/04/CGI/cgi2.html (dito)
http://www.linux-magazin.de/ausgabe/1998/05/CGI/cgi3.html (dito)
http://www.linux-magazin.de/ausgabe/1998/06/CGI/cgi4.html (dito)
http://www.teamone.de/selfhtml/tchg.htm (Naehere Informationen zum File-Button)
http://www.roxen.com/rfc/rfc1867.html (RFC1867 - Form based file upload in HTML)

Ergänzungen, Kommentare

ReneeBaecker - 08 Mar 2004 - Link zu Strat's Homepage geändert

Kommentare werden am besten in folgender Form vorgenommen, damit sie im Inhaltsverzeichnis angezeigt werden:
       ---### Main.??? - 14 Jul 2003 - Betreff
      

UtilFaqSubForm edit

Titel Wie kann ich ein Upload-Skript erstellen?
Autor Darkmage & perl-community
Bereich FaqCGI
Tags CGI, Upload
Topic revision: r10 - 2009-07-12 - 00:10:48 - HaraldBongartz
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um WebBottomBarExample">Rückmeldung.