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!
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

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