CGI und Perl
Description: Einführung in die CGI-Programmierung mit Perl im Internet. Vorkenntnisse der Programmierung sind notwendig.
Keywords: Multimedia WWW Internet Webdesign CGI Perl Programmierung

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

- alle "+"-Zeichen in Leerzeichen wandeln
- alle "%xx"-Sequenzen in einzelne Buchstaben mit dem ASCII-Wert "xx" wandeln. "xx" ist da bei eine Hexadezimalzahl (Beispiel: "%3D" wird zu "=")

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:

Zeichen | Code | Zeichen | Code | Zeichen | Code | Zeichen | Code
--- | --- | --- | --- | --- | --- | --- | ---
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:

- Bei der GET-Methode wird die in der <FORM ACTION="URL"> angegebene URL genommen und die codierten Eingabedaten angehängt, getrennt durch ein "?". Dies könnte zum Beispiel so aussehen:

        http://.../cgi-bin/testcgi.pl?Text=Hallo+dies+ist+ein+Test&Zeichen=%25

  Diese URL wird beim Absenden der Anfrage auch durch den Browser angezeigt. Die Nachteile der GET-Methode sind leicht ersichtlich: Es eignet sich nur bei relativ geringen Datenmengen und die URLs werden unleserlich. Der Vorteil liegt darin, daß diese Darstellung für den CGI-Programmierer sehr hilfreich sein kann beim Debugging seinen Scriptes. Außerdem ist GET schneller als POST.
  Normalerweise benutzt man `GET`, um kleine Datenmengen zu übertragen, z. B. einen Suchbegriff - vielleicht mit ein paar Parametern versehen. Dabei dürfen nicht zu viele Daten übertragen werden, denn die Maximallänge der URL variiert von Browser zu Browser. GET soll auf dem Server bis auf unvermeidbare Kleinigkeiten (z. B. Eintrag ins Logfile) keine Änerungen bewirken und auch keine Nebenwirkungen haben (etwa, wenn der Nutzer mehrmals bei seinem Browser auf "Reload" klickt). GET-URLs werden auch in der Browser-History und in den Server-Logs gespeichert, weshalb sie keine kritischen Infos enthalten sollten (Kundendaten, Kreditkartennummern, Passwörter etc.). Insbesondere gehören administrative Funktionen nicht in GET-Requests.

  Bei GET-Übermittlungen liegt das Ergebnis in der Environment-Variable `QUERY_STRING`.

- POST ist eine Nachricht an den Server, die eher einem Paket entspricht (GET ist da eher die Postkarte). Bei der POST-Methode bleibt die in <FORM ACTION="URL"> angegebene URL unverändert. Die Daten werden für den Benutzer unsichtbar an den Server übertragen. Dies hat aber den Nachteil, daß man die URL nicht bookmarken kann: Während man z. B. bei einer Suchmaschine, welche mit GET arbeitet, bestimmte Begriffe als Link in die Bookmarks eintragen kann, und so immer einen aktuellen Link auf einen Suchbegriff haben kann, kann man dies bei Post nicht machen. Schon diese Tatsache spricht dafür, Suchanfragen etc. per GET zu schicken, Bestellungen, Dateien etc. jedoch per POST.
  `POST` wird daher normalerweise dafür verwendet, dem Server eine größere Menge Daten zu übermitteln. Die Menge der übertragenen Daten ist im Gegensatz zu `GET` nicht begrenzt.

  Bei POST-Übermittlungen muß man die Formulardaten von `STDIN` lesen. Die genaue Anzahl der übermittelten Bytes steht dabei in der Variablen `CONTENT_LENGTH`.

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:

- Wird das Script ohne Parameter gerufen, gibt es eine Standardseite aus. Meist handelt es sich dabei um ein Formular.
- Der Benutzer füllt das Formular aus und schickt es ab. Das Formular kann natürlich auch auf einer HTML-Seite angeboten werden (dann entfällt der erste Punkt).
- Der Server empfängt die Anfrage und ruft das Script - diesmal mit Parametern - auf.
- Das Script verarbeitet die Parameter und gibt eine Ausgabe dazu aus.

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:

- `$SECURE` enthält die als nicht riskant betrachteten Zeichen (hier nur Buchstaben und Ziffern).
- `$DANGEROUS` enthält die zu maskierenden Zeichen, die zwar zulässig, aber potentiell gefährlich sind. Sie werden mit einem Backslash maskiert.

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

- Perl-Scripts kann man auf Syntaxfehler überprüfen (Warnings, Check): `perl -wc myscript.cgi`.
- Im Browser erscheint nichtdie gewünschte Ausgabe des Scripts, sondern sein Quellcode oder der Browser zeigt das Download-Fenster an: Die Ausführung von CGI-Programmen ist generell nicht möglich, Zugriffsrechte (755) wurden nicht gesetzt oder das Script steht im falschen Verzeichnis steht.
- Achten Sie drauf, daß der Pfad zum Perl- (oder Shell-) Interpreter in der ersten Scriptzeile richtig gesetzt ist.
- Einer der häufigsten Fehler ist die falsche Übertragung des Scripts per FTP. Das Script muß im ASCII-Modus auf den Server übertragen werden. Wird dies nicht gemacht, bleiben eventuell Steuerzeichen am Zeilenende (Carriage Return) übrig.
- Natürlich muß man kontrollieren, ob die im erzeugten HTML-File aufgerufenen Links so gültig sind - egal ob sie relativ oder absolut angegeben sind.
- Hat das CGI-Script die richtigen Unix-Zugriffsrechte?
- Liegt das Script an der richtigen Stelle? Jeder Webserver ist anders konfiguriert. Einige erlauben es, CGI-Scripts ins gleiche Verzeichnis wie die Webseiten zu packen, die Dateinamen müssen dann mit ".cgi" aufhören. Andere wollen die CGI-Scripts in einem speziellen Verzeichnis haben, meist "cgi-bin".
- Lassen Sie das Script von der Kommandozeile aus laufen und beobachten Sie, was passiert. Weit kommt man damit allerdings meistens nicht, weil die Environment-Variablen nicht belegt werden.
- Sollte das Script auf andere Dateien zugreifen, müssen die Zugriffsrechte auch für diese Dateien richtig gesetzt sein. Eine Adressen-Datenbank die von einem Script ausgelesen wird, als auch das Verzeichnis sollten für alle lesbar sein. Wenn ein Script in Dateien schreibt (z. B. Gästebuch), muß die Datei und ihr Verzeichnis zusätzlich für den User Schreib-Rechte haben, unter dem der Webserver-Prozess läuft (z. B. "wwwrun").

### 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.

- GET hängt die Eingabedaten an die URL an (Längenbeschränkung, nur praktikabel bei wenigen Eingaben),
- POST erzeugt einen eigenen Datenstrom.

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.

- Beispiel 1: Eingabezeile von 40 Zeichen Länge:
  `<INPUT TYPE="text" NAME="Nachname" SIZE=40 VALUE="No Name">`

  Ähnich wirken auch die Typen URL, TEL, EMAIL, DATE, TIME und DATETIME die bei mobilen Geräten die passende Eingabefunktion einblenden. Beim Type PASSWORD werden statt der eingegebenen Zeichen Bullets oder Sternchen angezeigt.

- Beispiel 2: Verstecktes Eingabefeld, das z. B. zur Identifikation des Formulars dienen kann:
  `<INPUT TYPE="hidden" NAME="Status" VALUE="form1">`

- Beispiel 3: Drei Radio-Buttons, Knopf 1 ist gedrückt:

         <INPUT TYPE="radio" NAME="Radiobutton" VALUE="1" CHECKED>Knopf 1
         <INPUT TYPE="radio" NAME="Radiobutton" VALUE="2">Knopf 2
         <INPUT TYPE="radio" NAME="Radiobutton" VALUE="3">Knopf 3

  Knopf 1 Knopf 2 Knopf 3

- Beispiel 4: Checkbox mit 4 Knöpfen, Knöpfe 1 und 2 gedrückt:

         <INPUT TYPE="checkbox" NAME="Checkbox" VALUE="1" CHECKED>K. 1
         <INPUT TYPE="checkbox" NAME="Checkbox" VALUE="2" CHECKED>K. 2
         <INPUT TYPE="checkbox" NAME="Checkbox" VALUE="3">K. 3
         <INPUT TYPE="checkbox" NAME="Checkbox" VALUE="4">K. 4

  K. 1 K. 2 K. 3 K. 4

- Beispiel 5: Suchfeld: Dies ist für Eingabe von Suchbegriffen vorgesehen. Es unterscheidet sich vom Typ TEXT nur unwesentlich. Hier ist ein Suchfeld zu sehen, das als Eingabemöglichkeiten nach den ersten Buchstaben Vogelnamen aus einer Liste vorschlägt. Es können aber auch Begriffe eingegeben werden, die nicht zu den Vorschlägen gehören.

      <INPUT TYPE="search" LIST="Voegel" />

      <DATALIST ID="Voegel">
        <OPTION VALUE = "Amsel">
        <OPTION VALUE = "Drossel">
        <OPTION VALUE = "Fink">
        <OPTION VALUE = "Star">
        <OPTION VALUE = "Meise">
        <OPTION VALUE = "Spatz">
        <OPTION VALUE = "Schluckspecht">
        <OPTION VALUE = "Schnapsdrossel">
        <OPTION VALUE = "Vollmeise">
        <OPTION VALUE = "Schabrackenschriller">
      </DATALIST>

- Beispiel 6: Ein Eingabefeld vom Typ NUMBER beschränkt die Eingabe auf Zahlen und erlaubt die Angabe von Grenzen und Schrittweite. Folgende Attribute können verwendet werden: MAX: Maximalwert, MIN: Minimalwert, STEP: Schrittweite beim Betätigen der Scrollbuttons, VALUE: Voreinstellung des Wertes.

      Länge in cm:
      <INPUT ID="len" TYPE="number" MIN="100" MAX="500" STEP="1" VALUE="250">
      Breite in cm:
      <INPUT ID="wid" TYPE="number" MIN="100" MAX="200" STEP="0.5" VALUE="100">

  Länge in cm: Breite in cm:

- Beispiel 7:Mit dem Eingabe-Typ COLOR haben Sie einen Farbwähler im Browser. Wenn Sie auf das Farbfeld klicken, öffnet sich ein Popup-Fenster, in dem Sie Farben auswählen, definieren und hinzufügen können. Benutzer älterer Browser sehen hier ein Textfeld, in das sie die entsprechenden Hex-Werte eintragen können.

      <INPUT TYPE="color" NAME="color" VALUE="#FF00FF">

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

- Beispiel für die Definition einer Textarea:

         <TEXTAREA NAME="Textarea" ROWS=5 COLS=40>
         Beliebiger Text, der im Textfeld steht.
         </TEXTAREA>

Beliebiger Text, der im Textfeld steht.

#### 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:

- Er übernimmt die Aufgabe des `SUBMIT`-Feldes und
- übermittelt zusätzlich die Koordinaten der Stelle, auf die im Bild geklickt wurde. Die entsprechenden Formularvariablen tragen die Namen "x" und "y".

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