Grundlagen CGI-Programmierung mit Perl


von Prof. Jürgen Plate

3 Common Gateway Interface

Das Common Gateway Interface (CGI) beschreibt nur ein Protokoll, nicht eine bestimmte Programmiersprache. CGI-Programme lassen sich somit in jeder auf dem Server verfügbaren Programmiersprache erstellen. Häufig wird jedoch die Sprache Perl verwendet, da sie plattformübergreifend verfügbar ist und sich besonders zur Manipulation von Texten eignet - und das ist meist die Hauptanwendung für CGI-Programme. Ein CGI-Programm (wegen der Interpretersprache Perl auch oft "CGI-Script" genannt) ist nichts besonderes. Es ist ein Programm, das von der Standardeingabe (stdin) lesen und auf die Standardausgabe (stdout) schreiben kann. Wer in der Lage ist, ein solches Programm zu schreiben, kann auch CGI-Scripts erstellen. Programmierkenntnisse sind also erforderlich, wobei es egal ist, ob es sich um ein Unix-Shell-Script, um ein C-Programm, ein Perl-Script oder etwas anderes handelt.

CGI ist, wie gesagt, keine Sprache. Es ist vielmehr ein einfaches Protokoll, das der Kommunikation zwischen HTML-Formularen und einem Programm dient. Ein CGI-Script kann in jeder Sprache geschrieben sein. Diese Kurzanleitung konzentriert sich daher auch auf die Verarbeitung von HTML-Forms. Einige Details werden nicht behandelt, dafür kommt man um so schneller zum Ziel und deckt 90% der üblichen Anwendungen ab.

Ein CGI-Script führt die folgenden typischen Schritte aus:

  1. Lesen der vom Clienten erzeugten Daten (entweder von der Parameterzeile des Programms oder von der Standardeingabe.
  2. Verarbeiten der Daten
  3. Schreiben einer Antwort in HTML auf die Standardausgabe.
Eine URL für ein CGI-Script sieht genau wie eine normale URL aus. Wie weiß der Server aber, daß es sich um eine ausführbare Datei und nicht um eine normale HTML-Seite handelt? Das ist ganz einfach. In der Konfigurationsdatei wurde eine Exec-Regel definiert. Sie zeigt auf das Verzeichnis, in dem sich die Scripte befinden.

3.1 Daten empfangen

Sofort stellt sich die Frage, "Wie kann man dem Script Anfragen mitteilen?". Dazu könnten einfach Parameter an die URL angehängt werden (siehe Beispiel unten). Mit dem Fragezeichen werden die Parameter vom Programmnamen getrennt. Alles, was danach folgt, wird als Parameter erfaßt. In einer Parameterzeile dürfen keine Leerzeichen stehen, weshalb sie durch ein "+"-Zeichen ersetzt werden. Sonderzeichen (Code > 127) werden durch ein Prozentzeichen (%) und den hexadezimalen Code des Zeichens ersetzt. Man kann also z. B. für ein Shop-Programm ein Bestell-Link der folgenden Form in eine Webseite einfügen:
<A HREF="http://shop.server.de/cgi-bin/shopper.pl?prodid=23481227">Bestellen</A>
Genauso häufig sind Daten, die durch die Eingabe in ein Formular erzeugt werden. Wenn der Client seine Daten übermittelt (Submit-Knopf im Formular), erhält das Script alle erzeugten Daten als einen Satz von Name-Wert-Paaren. Der Name ist jeweils der, den man beim INPUT-Tag (bzw. beim SELECT- oder TEXTAREA-Tag) festgelegt hat, die Werte sind das, was der Anwender ins Formular eingetragen oder gewählt hat. Dieser Satz von Name-Wert-Paaren wird in einem einzigen langen String übermittelt, den das CGI-Programm auflösen muß. Das ist nicht sehr kompliziert, und es gibt viele fertige Routinen dazu. Der Aufbau des Strings ist recht einfach:
     name1=wert1&name2=wert2&name3=wert3
Man muß den String also einfach beim &-Zeichen zerlegen. Dann erhält man die Paare
name1=wert1
name2=wert2
name3=wert3
In den entstandenen Häppchen muß man nun noch

Das kommt daher, das der übermittelte String in der URL des CGI-Programms codiert ist. Bei einer Form der Übermittlung zum Server wird er durch "?" getrennt an die URL angehängt, was Sie beispielsweise beim Bedienen einer Suchmaschine in der URL-Zeile des Browsers sehen kann. Der Anwender muß aber nach wie vor die Möglichkeit haben, &-Zeichen und Gleichheitszeichen zu übergeben - deshalb die Codierung durch "%xx". Die folgende Tabelle zeigt die Zeichencodierung:

ZeichenCodeZeichenCode ZeichenCodeZeichenCode
Leerz.+ !%21 "%22 #%23
$%24 %%25 &%26 '%27
(%28 )%29 +%2B ,%2C
/%2F :%3A ;%3B <%3C
=%3D >%3E ?%3F [%5B
\%5C ]%5D ^%5E "%60
{%7B |%7C }%7D ~%7E
°%A7 Ä%C4 Ö%D6 Ü%DC
ß%DF ä%E4 ö%F6 ü%FC

Das Anhängen der Daten an die URL ist aber nicht die einzige Möglichkeit - die Methode hätte auch ihre Grenzen bei grossen Datenmengen, etwa einer hochzuladenden Bilddatei. Deshalb kennt das ZTTP-Protokoll mehrere Möglichkeiten. Von wo das CGI-Programm den Formularinhalt erhält, hängt von der Methode ab, mit der die HTTP-Form übermittelt wurde:

All das geschieht hinter den Kulissen. Für den CGI-Programmierer funktionieren GET und POST fast gleich und sind gleich einfach zu benutzen. Der Vorteil von POST ist, daß man beliebig viele Daten übertragen kann. Der Vorteil von GET ist, daß alle Daten in eine URL gebastelt sind - man kann auf sie also verweisen oder sie in die Bookmarks aufnehmen.

Hier sind noch einmal die Schritte aufgeführt, die ein CGI-Script normalerweise durchlebt, wenn es vom Benutzer aufgerufen wird:

Damit sind die wesentlichsten Merkmale eines CGI-Scriptes erklärt. Alles was man tun muß, um selber ein CGI-Script zu schreiben, ist diese Punkte umzusetzen. Wie man das am besten tun kann, soll in den folgenden Abschnitten näher erklärt werden.

3.2 Dem Client eine Antwort geben

Der Browser sendet die Anfrage an ein CGI-Programm genauso ab, wie die Anforderung eines HTML-Dokuments und er werwartet natürlich auch, daß der Server mit entsprechenden Daten antwortet. Bei CGI wird jedoch keine HTML-Seite gesendet, sondern das Programm erzeugt die Daten dynamisch. Als erstes gibt es die Zeile
Content-Type: text/html
gefolgt von einer Leerzeile auf die Standardausgabe aus. Nun läßt es eine normale HTML-Antwort folgen, die es ebenfalls auf die Standardausgabe ausgibt. Wenn das Script beendet ist, sieht der Anwender die so erzeugte Seite. Das Script liegt in einem speziellen Verzeichnis auf dem Server (meist "cgi-bin"). Der Content-Type kann natürlich variieren, es sind alle gebräuchlichen und vom Browser akzepierten MIME-Typen erlaubt. Versuchen wir es mal mit einem einfachen Beispiel.

Die beiden folgenden Programme liefern jeweils nur ein "Hello World" als HTML-Seite. Es gibt jeweils eine C- und eine Perl-Version. Zuerst die C-Version:

/************************************************/
/**  hello.c -- simple "hello, world" fuer cgi **/
/************************************************/
#include <stdio.h>
int main(void) 
  {
  /** CGI response header **/
  printf("Content-type: text/html\n\n") ;
  /** HTML response page **/
  printf("<html>\n") ;
  printf("<head><title>CGI Output</title></head>\n") ;
  printf("<body>\n") ;
  printf("<h1>Hello, world.</h1>\n") ;
  printf("</body>\n") ;
  printf("</html>\n") ;
  return(0) ;
  }

Und nun die Perl-Version:

#!/usr/bin/perl
#
# CGI response header 
print "Content-type: text/html\n\n" ;
# HTML response page
print <<EOF ;
<html>
<head><title>CGI Results</title></head>
<body>
<h1>Hello, world.</h1>
</body>
</html>
EOF
exit;

Wer wissen möchte, wie mühsam die CGI-Programmierung in C ist, kann einen Blick in das C-Skript werfen.

3.3 Ein Bild (oder andere Binärdaten) als Antwort ausgeben

Die meisten CGI-Scripts geben HTML-Daten zurück, man kann aber auch Bilder, Sounds oder andere Binärdateinen zurückgeben. Dazu zählen auch beispielsweise Word- oder Exel-Dokumente. Man sollte nur den richtigen MIME-Type in der Content-type:-Zeile stehen haben - natürlich wieder von einer Leerzeile gefolgt. Anschließend können die rohen Binärdaten folgen. Bei einer HTML-Antwort ist das HTML-Text. Bei Bildern, Audio oder Video sind es Binärdaten. Ein GIF zum Beispiel gibt man so zurück:
Content-type: image/gif

GIF89a&%--- binärer Inhalt des GIF hier ---......

Die HTML-Datei beim Client kann ein solches Script-generiertes Bild zum Beispiel wie folgt aufrufen:

<IMG SRC="gifmaker.cgi?param1=wert1">

Für eine solche Aktion gibt das CGI-Programm in der Regel die "Content-type"-Zeile und die darauf folgende Leerzeile aus und kopiert dann die Binärdatei auf die Standardausgabe, z. B.:

#!/usr/bin/perl

my $file = ".....";  # Name und Pfad der GIF-Datei
my $buffer;

print "Content-type: image/gif\n\n";
open GIF, $file) || die "Cannot open $file\n";
print $buffer while (read(GIF,$buffer,16384));
close(GIF);

Wenn die HTML-Antwort des Scripts immer die gleiche ist oder wenn man mit einer von mehreren vorhandenen Dateien antworten möchte, könnte man das 'Location'-Antwortprinzip hilfreich finden. Damit kann man den Browser auf eine andere URL umleiten, was eigentlich für umgezogene Seiten gedacht ist. Hier ein Beispiel. Das Script sollte generell die folgenden drei Zeilen ausgeben:

Status: 302 Found
Location: response.html
URI: <response.html>

gefolgt von einer Leerzeile. Der Browser des Clients wird daraufhin die Datei "response.html" laden, als sei es die Antwort des CGI-Scripts. Hier kann man natürlich relative und absolut Adressen angeben. In diesem Fall erzeugt man keinen Content-type:-Header!

Normalerweise bindet man CGI-Skripts als "action" in ein Formular ein. Diese Vorgehensweise erfordert jedoch, daß der Benutzer explizit den Submit-Button aktiviert. Sie ist somit denkbar ungeeignet, automatisch Benutzer-Daten abzuspeichern (z. B. für User-Tracking oder Erfassung von Klick-Raten). Mit einem kleinen Trick aktiviert der Server das Skript jedoch automatisch. Dazu bindet man es als Bild in den HTML-Text aller überwachten Seiten ein:

 
<img src="/cgi-bin/klick.cgi">
Damit der Browser keinen Fehler meldet, weil er kein Bild geliefert bekommt, lenkt das CGI-Skript den Request mit
print "location: //icons/tolles.gif\n\n"; 
auf ein existierendes GIF-Bild um.

3.4 Sicherheit und CGI-Scripts

Man muß sich klar machen, daß ein CGI-Script ein Programm ist, daß jeder Mensch dieser Welt auf unserem eigenen Rechner laufen lassen kann. Gerade bei kommandozeilenorientierten Betriebssystemen (Unix) muß man sehr aufpassen, in seinem Script keine großen Lücken zu lassen.

Dabei gilt die Regel: Traue keiner Benutzer-Eingabe! Wie Sie oben gesehen haben, muß der Anwender kein Formular ausfüllen, sondern er kann alle Eingaben für Ihr Script auf der URL-Zeile zusammenbasteln. Zum Beispiel sollte man nie vom Anwender übermittelte Daten direkt als Kommando ausführen, ohne die Daten zu prüfen. Hacker riechen solche Löcher! Betrachten Sie dazu folgendes CGI-Script mit dem man Benutzerdaten per "finger"-Kommande einholen kann. Ein Perl-Script könnte folgendermaßen aussehen:

system ("finger $username");
Wenn der User nun "james; rm -rf /'" als Usernamen eingibt, wird daraus
system ("finger james; rm -rf /");
was so viele Dateien wie möglich löscht. Auch wenn der WWW-Server mit relativ beschränkten Zugriffsrechten läuft, kann doch die eine oder andere Datei "erwischt" werden. Die Eingabe sollte also daraufhin geprüft werden, ob "gefährliche" Zeichen enthalten sind und dann zurückgewiesen werden.

Es ist für einen Hacker ja ganz einfach, irgendwelche FORM-Variablen an ein Script zu schicken - mit irgendwelchen Werten (auch Steuerzeichen). Ein Script sollte auf solche Daten nicht reagieren. Er braucht dzu nicht einmal ein Formular, bei Verwendung der Methode GET genügt es, alles in der URL-Zeile einzutippen. Ein Hacker kann auch einfach lokal den HTML-Quelltext verändern, zusätzliche Dateien oder auch Kommandos in sein Menü einfügen und dann das geänderte Formular abschicken.

Oft leitet ein Skript die Eingabe auch an Programme weiter, ohne daß der Programmierer wichtige Details über diese kennt. So hat das Programm mail wie viele andere Unix-Programme eine rudimentäre Benutzerschnittstelle, um Benutzern von Terminals das Leben zu erleichtern: Gibt man in der ersten Spalte einer Zeile eine Tilde (~) ein, so wird der Rest der Zeile vom Betriebssystem interpretiert. Jedes Skript, das Benutzereingaben ungeprüft an dieses Utility weiterreicht, bietet e inem Hacker damit sehr komfortable Möglichkeiten, weil er gleich mehrere Zeilen in einem Rutsch auf dem Rechner ausführen lassen kann.

Alle Daten, Parameter oder Anfragen, die den Server von außen erreichen, sind also als potentiell gefährlich anzusehen. Überprüfungen im Browser reichen aus o. g. Gründen nicht. Auch Cookies bieten keinen Schutz vor Manipulation. Es ist nämlich möglich, mit Hilfe von verbreiteten Modulen einen Web-Client in Perl zu schreiben, der dann problemlos Cookies setzen kann.

Bei der Eingabeprüfung gibt es zwei Strategien: Entweder man testet, ob die Eingabe explizit erlaubt ist, also zum Beispiel nur aus Buchstaben und Zahlen besteht. Der andere Weg ist die Kontrolle, ob die Eingabe explizit verbotene Dinge bewirkt. Falls man zur zweiten Strategie greift, muß man genau wissen, welche Eingaben zum Beispiel in der aufzurufenden Shell unerwünschte Effekte haben. Gefährlich sind folgende Zeichen:

; < > * | " & $ ! # ( ) [ ] { } : ; ' ` ? ^ \ 
Diese Zeichen dienen in der einen oder anderen Sprache dazu, mehrere Befehle voneinander zu trennen oder die Ein- und Ausgabe eines externen Programms umzuleiten. Dabei ist die obige Liste noch unvollständig; so kann beispielsweise auch ein Zeilenwechsel (new line) oder ein Zeichen mit ASCII-Darstellung > 128 bestimmte Programme durcheinanderbringen. Die sicherere Strategie ist deshalb der Test auf explizit erlaubte Zeichen.

Externe Programme sollten mit Vorsicht eingesetzt werden. Bei vielen gibt es die Möglichkeit des Zugriff auf die Shell. So bildet z. B. beim mail-Programm die Angabe des Adressaten auf der Kommandozeile eine Sicherheitslücke, den dort kann zum einen eine ganze Liste plaziert werden oder wieder Kommandos verkettet (wie oben bei "finger"):

open( MAIL, "| mail $addressat");
print MAIL "Subject: Fehlerreport\n\n";
print MAIL $emailtext;
close MAIL;
In jedem Falle ist hier das Programm sendmail vorzuziehen. Dieses Tool bietet mit der Kommandozeilen-Option -t die Möglichkeit, des Aufrufs ohne Parameter. Das sieht dann etwa so aus:
open (MAIL, "| /usr/lib/sendmail -it");
print MAIL "To: $addressat\n";
print MAIL "From: webmaster\@netzmafia.de\n",
print MAIL "Subject: Fehlerreport\n\n";
print MAIL $emailtext;
close MAIL;
Trotzdem sollte man auf die zusätzliche Überprüfung der E-Mail-Adresse nicht verzichten.

Dann sollte man im Quelltext suchen, wo Interpreter aufgerufen werden. In Perl kommen alle Aufrufe der Funktionen eval, open, system, exec und glob in Frage, aber auch die sogenannten Backticks wie zum Beispiel in $line = "grep $word $file"; In C heißen die kritischen Funktionen system, popen und exec. Normalerweise sind dies nur wenige Stellen im Programm. An diese Funktionen darf ein CGI-Skript auf keinen Fall ungeprüfte Benutzereingaben als Argumente übergeben. In C sollte man zusätzlich die Speicherverwaltung unter die Lupe nehmen und prüfen, ob statische Puffer verwendet werden.

Die meisten Web-Server starten zwar typischerweise als "root". Diese Rechte benötigen sie, um die Netzwerkverbindung herzustellen. Wenn sie dann tatsächlich Benutzeranfragen bearbeiten, benutzen sie eine andere Benutzeridentität mit weniger Rechten, unter Unix typischerweise als Benutzer "nobody" oder "wwwrun". CGI-Skripte laufen dann auch unter dieser Benutzerkennung. Ein Hacker, der ein Sicherheitsloch in einem CGI-Skript ausnutzt, führt seine Kommandos mit niedrigen Privilegien aus. Aber das schließt nicht allen Mißbrauch aus. Selbst dieser Benutzer ist trotzdem zu vielen Dingen berechtigt.

Taint checking

Taint Checking wird mit der Kommandozeilenoption "-T" eingeschaltet, also zum Beispiel durch die Anfangszeile #!/usr/bin/perl -wT. Das Wort "taint" läßt sich wohl am besten mit "verdorben" übersetzen. Ist Taint Checking eingeschaltet, sieht der Perl-Interpreter grundsätzlich alle Daten als "verdorben" an, die von außen in das Skript kommen. Jede Variable, die mit verdorbenen Daten in Berührung kommt, gilt ebenfalls als verdorben. Wenn das Skript jetzt eine Funktion aufruft, die Dateien manipuliert oder Daten an die Shell weitergibt, so überprüft der Interpreter zunächst, ob ihre Argumente "verdorbene" Daten enthalten. Falls ja, beendet er das Skript mit einer Fehlermeldung.

Irgendwie muß das Skript aus einer verdorbenen Benutzereingabe eine gute machen. Wie bei Lebensmitteln muß man sich die verdorbenen Daten gut ansehen und verdächtige Stellen großflächig entfernen. Hierfür bietet Perl reguläre Ausdrücke. Der im folgenden vorgestellte reguläre Ausdruck dient z. B. zur Überprüfung, ob ein eingegebener Text eine halbwegs gültige EMail-Adresse darstellt.

/^([a-zA-Z0-9\-\.]+)@([a-zA-Z0-9\-\.]+\.[a-z]+)$/ 
Er fängt zumindest die gröbsten Schnitzer ab. Wenn Perl das durch den regulären Ausdruck beschriebene Muster erkennt, kann man das Ergebnis einer Variablen zuweisen: $email = "$1\@$2"; Die Variable $email kann dann gefahrlos für den Aufruf eines externen Mail-Programmes verwendet werden.

Das Null-Byte

Perl behandelt - anders als C - jedes Zeichen gleich. Für Perl ist das Null-Zeichen als Ende einer Zeichenkette ein Zeichen wie jedes andere. Ein Problem entsteht, wenn das Null-Byte sich innerhalb eines Dateinamens (oder eines Teils davon) befindet. Letztendlich landet die Programmausführung beim entsprechenden Systemaufruf und dort ist das Null-Byte die Markierung für das Stringende. Daher kann ein im Eingabeparameter platziertes Null-Byte zu seltsamen Effekten oder Sicherheitslücken führen. Dieses Problem lässt sich mit einem regulären Ausdruck lösen, der alle Null-Bytes entfernt: s/\0//g;

Ähnliche Probleme können auch beim Newline-Zeichen (\n) oder beim Carriage Return (\r) auftreten. Ebenso müssen der Backslash und weitere Zeichen ausmaskiert werden.

Pipes

Normalerweise ist es recht komfortabel, wenn man in Perl die Ausgabe eines Programms direkt lesen kann oder wenn man im eigenen Script erzeugte Daten direkt in ein anderes Programm "pipen" darf. Bei CGI-Skripten kann sich dieses Feature von open() als fatal herausstellen. Das Hinzufügen eines einzelnen Pipe-Symbols ändert das Verhalten von open(). Deshalb ist auch das Pipe-Symbol (|) in Eingabewerten als gefährlich einzustufen.

Die sicherste Methode ist natürlich, Dateinamen generell unabhängig von Eingabeparametern zu machen. Man kann im Programm (oder in einer Konfigurationsdatei) die Zuordnung Parameter - Dateiname als Hash vorsehen. Also nicht z. B.:

 ...
$dat = FORM{'topic'};
$filename = $PATH . $dat . '.html';
open(HELP,$filename);
 ...
Im obigen Beispiel gibt es noch mehr Haken. So wird nicht geprüft, ob es die Datei gibt und es wird auch nicht explizit der Modus angegeben. Besser ist da schon folgender Code:
 ...
my %Filenames = (
  'eins' => '/home/www/helpfiles/one.html',
  'zwei' => '/home/www/helpfiles/two.html',
  'drei' => '/home/www/helpfiles/three.html',
  );
 ...
$dat = FORM{'topic'};
$filename = $Filenames{$dat};
if (-e $filename) 
  {
  open(FILE,"<$filename") || goto form;
  ...
  } 
else 
  { errmsg("Keine Information zu $dat vorhanden!\n"); }
 ...

Eingabe-Maskierung und -Filterung

Sie können entweder die verbotenen Zeichen spezifizieren oder Sie definieren die ausdrücklich zulässigen Zeichen durch reguläre Ausdrücke. Es ist aber recht wahrscheinlich, daß Sie beim Filtern einige möglicherweise gefährliche Zeichen vergessen; deshalb wird die zweite Methode empfohlen.

Zuerst überprüfen Sie, ob die Eingabe nur die erlaubten Zeichen enthält. Als nächstes werden von den zugelassenen Zeichen diejenigen maskiert, die als gefährlich angesehen werden. Dazu ein Beispiel:

 ...

# Definition der Zeichenmengen
my $SECURE = '\w\d';
my $DANGEROUS = '&`\'\\|"*?~<>^(){}\$\n\r\[\]';

if ($input =~ m/^[$SECURE$DANGEROUS]+$/g) 
  {
  $input =~ s/([$DANGEROUS]+)/\\$1/g;
  }
else 
  {
  print "Bad input chars in $input\n";
  goto form;
  }
print "Result = [$input]\n";
 ...

 
Dieses Skript definiert zwei Zeichen-Mengen: Jede Anforderung, die ein Zeichen enthält, welches nicht in einem der beiden Zeichensätze enthalten ist, wird sofort zurückgewiesen.

3.5 Wenn das Script nicht läuft

3.6 Formulare

Der Inhalt von HTML-Dateien wird jeweils vom WWW-Server zum Benutzer am Client gesendet. Mit Hilfe von Formularen, die in HTML-Dateien stehen, können die Benutzer bestimmte Informationen am Client eingeben und an den WWW-Server senden. Diese Daten werden meistens von einem auf diesem Server laufenden CGI-Programm verarbeitet. Zum Beispiel:

<FORM METHOD="GET" ACTION="http://host.domain/cgi-bin/testmich">
Anmeldung zur Weihnachtsfeier der Fakultät am 18. Dezember 
<P>
<INPUT TYPE="radio" NAME="kommt" VALUE="ja" CHECKED> Ich komme.<br>
<INPUT TYPE="radio" NAME="kommt" VALUE="nein"> Ich komme nicht.
<P>
Name: <INPUT TYPE="text" NAME="Name" SIZE="40" MAXLENGTH="60"><BR>
Telefonnummer: <INPUT TYPE="text" NAME="tel" SIZE="20" MAXLENGTH="20">
<P>
<INPUT TYPE="submit" VALUE="Weg damit">
</FORM>

bewirkt eine Darstellung wie


Anmeldung zur Weihnachtsfeier des Fachbereichs am 18. Dezember

Ich komme.
Ich komme nicht.

Name:

Telefonnummer:


Anmerkung: Das in diesem Beispiel verwendete CGI-Programm gibt es nicht (schon, weil es den Rechner nicht gibt).

Eine 'echte' Anmeldungs-Prozedur würde die Zu- und Absagen mit Name und Telefonnummer in einer Datenbank speichern und dann die Bestätigung an den Client senden.

Das Senden einer solchen Antwort ist immer notwendig, denn der Benutzer muß auf seinem Bildschirm erkennen können, daß das Anklicken des Submit-Knopfes funktioniert hat. Im einfachsten Fall genügt eine kurze Meldung, daß die Eingabe verarbeitet wird, und ein Hinweis, daß der Benutzer mit der Back-Taste oder dem Back-Befehl seines Browsers zur vorherigen Information zurückkehren und weiterarbeiten kann.

Für genauere Informationen über die vom Server unterstützten Übertragungs-Methoden und die Übergabe der Eingabedaten an das CGI-Programm ist ein Studium der entsprechenden Fachliteratur nötig.
Als Methode können GET oder POST verwendet werden.

Die CGI-Programme müssen so geschrieben werden, daß Ihre Ausführung kein Sicherheitsrisiko für den Server-Computer darstellen kann, egal was für eventuell seltsame Eingaben von den Benutzern kommen. Inzwischen gibt es auch diverse fertige CGI-Programme zum Einsatz auf dem Server.

Nun etwas mehr Info zum Aufbau von Formularen. Zur Erinnerung: Formulare werden durch die Tags <FORM> ... </FORM> begrenzt, wobei über die Attribute METHOD und ACTION die Art der Verarbeitung und das Bearbeitungsprogramm auf dem Server spezifiziert werden:

<FORM METHOD="POST" ACTION="URL des Bearbeitungsprogramms" >

Zum Beispiel: <FORM METHOD="GET" ACTION="http://www.netzmafia.de/cgi-bin/formularcheck.cgi">

Die Zeile oben können Sie übrigens verwende, um ein Formular zu testen. Das Script liefert eine Liste aller Eingaben als Antwort.

Dmit sind wir auch schon bei den verschiednen Eingabe-Widgets angelangt. Innerhalb eines Formulars gibt es neben drei Eingabe-Tags, INPUT, SELECT und TEXTAREA.

Das INPUT-Widget <INPUT ...Attribute ...>

Dies ist das häufigste Widget und es hat zahlreiche Attribute:
TYPE="...."
Typ des Widgets. Als Typangaben sind möglich: CHECKBOX, HIDDEN, PASSWORD, RADIO, RESET, SUBMIT, TEXT, IMAGE, SEARCH und andere.
NAME="...."
Name des Eingabefeldes. Dieser Name wird im Datenstrom zurückgegeben.
VALUE="...."
Defaultwert (TEXT, HIDDEN), Wert des Menüpunkts (CHECKBOX, RADIO), Beschriftung des Feldes (RESET, SUBMIT).
SRC="...."
Quelldatei für einen IMAGE-Button.
CHECKED
Defaulteinstellung bei CHECKBOX und RADIO.
SIZE="...."
Feldgröße (Anzahl Zeichen) beim TEXT-Widget.
MAXLENGTH="...."
Maximallänge der Eingabe beim TEXT-Widget.
ALIGN="...."
Position des Textes neben einem Bild beim IMAGE-Tag.

Die folgenden Beispiele sollen die Anwendung des INPUT-Tags etwas erläutern. Das Passwort-Feld unterscheidet sich vom Textfeld nur dadurch, daß die Eingabezeichen als '*' geechot werden. Grundsätzlich hat jedes Feld einen Namen, der es dem Programm oder Script auf dem Server erlaubt, das Eingabefeld zu identifizieren. Bei RADIO-Buttons kann ein Wert aus dem Angebot ausgewählt werden, bei einer CHECKBOX deren mehrere.

Das TEXTAREA-Widget <TEXTAREA> .... </TEXTAREA>

erlaubt die Definition eines rechteckigen Eingabefeldes. Die Größe des Feldes auf dem Bildschirm wird durch die Attribute ROWS und COLS festgelegt; die Eingabe selbst kann umfangreicher sein. Zwischen <TEXTAREA> und </TEXTAREA> kann eine Textvorgabe stehen. Es gibt drei Attribute:
NAME="...."
Name des Eingabefeldes (wie bei INPUT).
ROWS="...."
Anzahl Zeilen des Feldes
COLS="...."
Anzahl Spalten des Eingabefeldes

Das SELECT-Widget <SELECT> .... </SELECT>

Selektionsfelder benötigen weniger Platz als Checkboxes oder Radiobuttons; sie gleichen Pull-Down-Menüs. Es gibt auch nur drei Attribute:
NAME="...."
Name des Feldes wie schon zuvor.
SIZE="...."
Anzahl der dargestellten Elemente. MULTIPLE erlaubt Mehrfachauswahlen. Fehlt die SIZE-Angabe, stellt sich das Widget als Pull-Down-Menü dar, von dem zunächst nur ein Menüpunkt sichtbar ist und das man zunächst öffnen muß (Pop-Up-Menü). Im anderen Fall zeigt sich das Menü als Scrollbar.
OPTION
Ein Menüfeld wird über das OPTION-Tag beschrieben, wobei der voreingestellte Menüpunkt über das SELECTED-Attribut ausgewählt wird. Beispiel:
<SELECT NAME="Pull-Down1" SIZE="6">
  <OPTION SELECTED>Feld 1
  <OPTION>Feld 2
  <OPTION>Feld 3
  <OPTION>Feld 4
  <OPTION>Feld 5
  <OPTION>Feld 6
</SELECTED>

<SELECT NAME="name" SIZE="1">
  <OPTION SELECTED>Feld 1
  <OPTION>Feld 2
  <OPTION>Feld 3
  <OPTION>Feld 4
  <OPTION>Feld 5
  <OPTION>Feld 6
</SELECTED>

Abschicken/Rücksetzen

Zum Rücksetzen aller Werte auf die Voreinstellungen dient der RESET-Type von INPUT und zum Absenden an den Server der SUBMIT-Button:
   <INPUT TYPE="reset" VALUE="Rücksetzen">
   <INPUT TYPE="submit" VALUE="Abschicken">
Sie können das Ganze mit Hilfe des Formularcheck-Scripts auf Netzmafia auch gleich testen. Wenn Sie auf "Abschicken" klicken, werden alle Daten aus den oben aufgeführten Formularfeldern angezeigt.

           

Der Image-Button

Ein Image-Button erfüllt zwei Aufgaben: Damit kann er auch für Anwendungen verwendet werden, die sonst Image-Maps brauchen - man könnte z. B. eine Landkarte als Submit-Button verwenden.
Beispiel:
<INPUT NAME="name" TYPE="image" SRC=bilder/Sender.gif" ALIGN="top">

Der folgende Button hat dieselbe Aufgabe, wie die Text-Version für "Abschicken" oben:

Alternativ könnte man eine sehr breiten Button machen, der als Skala angeklickt werden kann und bei dem man aus den Koordinaten den Eingabewert ermitteln kann. Die folgende Skala von 0 bis 100 (beispielsweise für Prozentwerte) ist 516 Pixel breit. Der Nullpunkt der X-Achse liegt bei Pixel 5, die 100er-Marke bei 505. Aus dem Eingabewert kann man dann leicht den Prozentwert ermitteln: Prozent = (x - 5)/5. Das ist natürlich nicht auf 1% genau, reicht aber meist aus.

3.7 Environment-Variablen

CGI-Scripts können auf etwa 20 Environment-Variablen zugreifen, z. B. QUERY_STRING und CONTENT-LENGTH. Ein paar sind besonders interessant:
CONTENT_TYPE
Für Anforderungen, an die Informationen gebunden sind, so wie bei HTTP POST und PUT, ist hier der Typ der Daten enthalten.
CONTENT_LENGTH
Dies ist die Länge der eben genannten Daten, die vom Klienten übergeben werden.
DOCUMENT_ROOT
Pfad des Dokumenten-Verzeichnisses.
HTTP_ACCEPT
Die MIME Typen, die der Klient akzeptiert, so wie sie im HTTP Header enthalten waren. Jeder Bestandteil der Liste sollte laut HTTP Spezifikation durch Komma getrennt sein. Format: type/subtype, type/subtype
HTTP_REFERER
URL der Seite, von der das Skript aufgerufen wurde.
HTTP_USER_AGENT
Name, Version und ggf. Plattform des Browsers.
HTTP_COOKIE
Enthält Cookie-Wert, wenn der Browser Cookies zu diesem Server gespeichert hat.
QUERY_STRING
Die Information, die einem "?" in einem URL, der das Skript referenziert, folgt. Man nennt sie die Query-Information. Die Variable sollte immer gesetzt sein, falls eine Query-Information vorhanden ist, unabhängig von der Kommandozeilendekodierung.
PATH_INFO
Genauere Pfad-Information (Ohne Server-Domainname). So ist es möglich, dem Script einen Pfad zu übergeben. Der muß dann hinter der URL des CGI-Scripts stehen:
http://www.myhost.com/mypath/myscript.cgi/path/info/here
würde PATH_INFO auf /path/info/here setzen. PATH_TRANSLATED liefert den vollständigen Pfad.
REMOTE_HOST
Diese Variable beinhaltet den Namen des Servers, der die Seite aufgerufen hat. Oft ist das nicht sehr Aussagestark - besser aber, als gar nichts.
REMOTE_ADDR
Diese Variable beinhaltet die IP-Adresse des Servers, der die Seite aufgerufen hat.
REMOTE_USER
Falls der Server Nutzer-Authentifizierung unterstützt und das Skript einem Schutz unterliegt, so wird hiermit der Nutzername übergeben, der authentifiziert wurde.
REMOTE_IDENT
Falls der Server RFC 931 Identifikation unterstützt, dann wird diese Variable auf den Namen des Nutzers, wie er vom Server übermittelt wurde, gesetzt. Die Benutzung dieser Variablen sollte auf die Protokollierung begrenzt sein.
REQUEST_METHOD
Beinhaltet die Methode, mit der das Script aufgerufen wurde. Normalerweise "GET" oder "POST".
SCRIPT_NAME
Die lokale URL des ausgeführten Scriptes. In den CGI-Spezifikationen ist unklar, ob ein führender Schrägstrich mit übergeben werden soll. Mit der folgenden Perl-Zeile hat man nie einen führenden Schrägstrich:
$ENV{'SCRIPT_NAME'}=~ s#^/## ;
Die komplette URL des ausgeführten Scriptes wäre in Perl also
http://$ENV{'SERVER_NAME'}:$ENV{'SERVER_PORT'}/$ENV{'SCRIPT_NAME'}
SERVER_NAME
Hostname und IP des Webservers.
SERVER_PORT
Der benutzte Port des Webservers (zumindest für diesen Request)
SERVER_PROTOCOL
Stand des Server-Protokolls im Format protokoll/revision
SERVER_SOFTWARE
Name und Version der Server-Software im Format name/version

3.8 HTTP-Statuscodes

Ein WWW-Server gibt auf jede Anfrag eine Status-Antwort zurück. Sie zeigt die Version des Servers an und gibt einen Ergebniscode zurück. Manchmal wird auch noch eine Meldung angehängt. Die erste Zeile sieht typischerweise so aus:
     HTTP/1.0 200 OK
wobei HTTP/1.0 die HTTP-Version ist, 200 ist ein Statuscode und OK die zugehörige Meldung. Es gibt natürlich viele andere Codes.

Statuscode zugehörige Meldung Bedeutung
100 Continue Fortfahren. Wird derzeit noch nicht verwendet.
101 Switching Protocols Protokoll wechseln. Wird derzeit noch nicht verwendet.
200 OK Der Server konnte die angeforderten Daten wie gewünscht versenden. Dies ist der Normalfall, wenn keine Probleme auftauchen.
201 Created Ein Objekt (z.B. eine Datei oder ein Verzeichnis) wurde auf dem Server erfolgreich angelegt. Beispielsweise, wenn die Anfrage des Browsers an den Server mit einer der HTTP-Übertragungsmethoden post oder put erfolgte.
202 Accepted Der Server hat die Anfrage des Browsers akzeptiert, liefert aber keine Daten als Antwort. Der Server schreibt die Daten statt sie zu senden in eine Datei und teilt in der Meldung mit, wo die Daten später zu finden sein werden.
203 Non-Authoritative
Information
Dieser Statuscode sollte von einem Server anstelle von 200 zurückgegeben werden, wenn es sich nicht um den Original-Server handelt, sondern beispielsweise um einen Proxy-Server. Der Web-Browser erfährt auf diese Weise, dass die Daten erfolgreich gesendet werden konnten, aber nicht vom Original-Server kommen und daher keine Garantie auf deren Aktualität besteht.
204 No Content Der Server hat die Anfrage erhalten, sendet jedoch keine Daten zurück. Gut verwendbar ist dieser Statuscode bei Verwendung in CGI-Scripts, die zwar etwas auf dem Server erledigen, aber keinen neuen HTML-Code an den aufrufenden Browser senden wollen. Aus Sicht des Anwenders bleibt der alte Bildschirminhalt bestehen.
205 Reset Content Der angegebene Server existiert nicht bzw. der Server, der diese Antwort gibt, ist nicht der angefragte Server und kann den angefragten Server nicht finden. Die angeforderten Daten können deshalb nicht versendet werden.
206 Partial Content Die angeforderten Daten werden in mehreren Portionen versendet. Mit Angaben zu content_length und content_range wird angegeben, wie viele Bytes von dem angeforderten Inhalt geliefert werden, und welcher Teil der Gesamtdaten.
300 Multiple Choices Die angeforderten Daten sind unter mehreren verschiedenen URIs vorhanden (Mirror). Anstelle der Daten werden die verfügbaren URIs als Liste übertragen. Der Web-Browser kann den Anwender anschließend in einem Dialog einen URI auswählen lassen.
301 Moved Permanently Die angeforderten Daten befinden sich nicht mehr unter dem URI, sie wurden dauerhaft auf eine andere Adresse verschoben. In der Statusmeldung wird angegeben, unter welchem URI sich die Daten jetzt befinden. Ein Web-Browser, der diese Antwort vom Server erhält, kann beispielsweise gleich die neue Adresse anfordern.
302 Moved Temporarialy Die angeforderten Daten wurden vorübergehend zu einem anderen URI verschoben. In der Statusmeldung wird angegeben, unter welcher Adresse sich die Daten derzeit befinden.
303 See Other Die angeforderten Daten sind unter einem angegebenen URI verfügbar und sollte von dort mit Hilfe der get-Methode angefordert werden. Dieser Statuscode ist für CGI-Scripts gedacht, die mit der post-Methode aufgerufen wurden und den Browser auf eine andere Ressource lenken wollen, die mit der get-Methode angefordert werden soll.
304 Not Modified Die angeforderten Daten haben sich seit dem angegebenen Zeitpunkt nicht geändert und werden deshalb nicht gesendet. Dieser Statuscode ist neben dem Code 200 einer der häufigsten in der Praxis. Er wird verursacht durch Web-Browser, die aufgrund ihrer Cache-Einstellungen Daten erst wieder nach einer bestimmten Zeit vom Original-Server laden.
305 Use Proxy Die angeforderten Daten sollen statt von diesem Server von dem in der Statusmeldung angegebenen Proxy-Server angefordert werden.
306 [Unused] Reserviert. Wird derzeit aber nicht verwendet.
307 Temporary Redirect Wie Statuscode 302. Gedacht für Fehlreaktionen einiger Browser auf 302.
400 Bad Request Die Anfrage enthält Syntaxfehler. Der Server kann die Anfrage deshalb nicht bearbeiten. Das kann beispielsweise vorkommen, wenn die Anfrage dadurch zustande kam, dass ein Anwender versuchte, einen URI händisch in die Adresszeile des Browsers einzugeben und dabei ungültige Zeichen verwendete.
401 Unauthorized Die angeforderten Daten sind zugangsgeschützt. Der Server kann die Daten nur senden, wenn eine gültige Zugangskennung, bestehend aus Benutzername und Passwort, bei der Anfrage mit gesendet wird. Das passiert in der Praxis immer dann, wenn eine Adresse aufgerufen wird, die z.B. durch .htacess zugangsgeschützt ist. Der Web-Browser zeigt dann, nachdem er diesen Statuscode erhalten hat, einen Dialog zum Eingeben von Benutzername und Kennwort an. Mit den eingegebenen Daten startet er dann eine neue Anfrage an den Server.
402 Payment Required Die angeforderten Daten sind kostenpflichtig. Der Server braucht eine Bestätigung der Zahlung. Derzeit wird dies noch nicht verwendet.
403 Forbidden Die angeforderten Daten sind zugangsgeschützt. Die angegebenen Zugangs-Daten sind ungültig, z.B., wenn zuvor der Statuscode 401 zurückgeliefert worden war und der Browser falsche Zugangsdaten gesendet hat.
404 Not Found Der angeforderte URI existiert nicht. Status 404 tritt immer dann auf, wenn ein Verweis auf eine nicht oder nicht mehr existierende Adresse auf dem Server führt (Tippfehler? Seite gelöscht?).
405 Method Not Allowed Die angegebene Übertragungsmethode ist auf dem Server nicht erlaubt. Die Daten werden deshalb nicht übertragen.
406 Not Acceptable Die Anfrage ist in dieser Form nicht akzeptabel. Die Daten werden deshalb nicht übertragen.
407 Proxy Authentication
Required
Der anfragende Client ist ein Proxy-Server. Die Daten werden nur übertragen, wenn er sich als gültiger Proxy-Server ausweist. Dieser Statuscode findet derzeit noch keine Verwendung.
408 Request Timeout Der Server hat eine erwartete Anfrage nicht innerhalb des dafür festgelegten Maximalzeitraums erhalten. Die Verbindung zum anfragenden Browser wird deshalb abgebaut.
409 Conflict Der Server kann die angeforderten Daten nicht senden, weil ein Konflikt mit einem anderen Prozess aufgetaucht ist.
410 Gone Die angeforderten Daten wurden zu einem anderen URI verschoben. Dem Server ist aber nicht bekannt, wohin.
411 Length Required Die Daten werden nicht gesendet. Sie können nur gesendet werden, wenn die Anfrage eine Angabe zu content_length enthält.
412 Precondition Failed Eine oder mehrere Bedingungen, die bei der Anfrage gestellt wurden, treffen nicht zu. Die angeforderten Daten werden deshalb nicht übertragen.
413 Request Entity
Too Large
Der Server kann die Anfrage nicht bearbeiten, weil diese zu viele Zeichen enthält. Die angeforderten Daten werden deshalb nicht übertragen.
414 Request-URL Too Long Der Server kann die Anfrage nicht bearbeiten, weil die angeforderte Adresse zu viele Zeichen enthält. Die angeforderten Daten werden deshalb nicht übertragen.
415 Unsupported Media Type Der Server kann die Anfrage nicht bearbeiten, weil er keinen Mime-Type für den angeforderten Datentyp kennt. Die angeforderten Daten werden deshalb nicht übertragen.
416 Requested Range
Not Satisfiable
Die Anfrage enthält Angaben, welcher Byte-Bereich von dem angeforderten URI übertragen werden soll. Sowohl der Anfangswert als auch der Endwert des angegebenen Bereichs liegen außerhalb des verfügbaren Bytebereichs. Die angeforderten Daten werden deshalb nicht übertragen.
417 Expectation Failed Die Anfrage enthält im expect-Feld bestimmte Wünsche, die der Server nicht erfüllen kann. Die angeforderten Daten werden deshalb nicht übertragen.
500 Internal Server Error Der Server kann die angeforderten Daten nicht senden, weil auf dem Server ein Fehler aufgetreten ist. Beispielsweise konnte das aufgerufene CGI-Script nicht gestartet werden oder es ist wegen eines Programmierfehlers abgestürzt.
501 Not Implemented Die Anfrage enthält Anforderungen, die der Server nicht bearbeiten kann, weil die Voraussetzunen dazu nicht implementiert sind.
502 Bad Gateway Zum Bearbeiten der Anfrage musste der Server einen anderen Server aufrufen, erhielt dabei jedoch eine Fehlermeldung. Die angeforderten Daten können deshalb nicht gesendet werden.
503 Service Unavailable Der Server kann die Anfrage wegen Überlastung nicht bearbeiten. Die angeforderten Daten können deshalb nicht gesendet werden.
504 Gateway Timeout Zum Bearbeiten der Anfrage musste der Server einen anderen Server aufrufen, erhielt dabei jedoch nach einem festgelegten Maximalzeitraum keine Antwort. Die angeforderten Daten können deshalb nicht gesendet werden.
505 HTTP Version
Not Supported
Der Server unterstützt die im HTTP-Header der Anfrage angegebene HTTP-Version nicht. Die angeforderten Daten werden deshalb nicht gesendet.

Zum vorhergehenden Abschnitt Zum Inhaltsverzeichnis Zum nächsten Abschnitt


Copyright © FH München, FB 04, Prof. Jürgen Plate
Letzte Aktualisierung: 31. Mär 2013