CGI
Dynamik für Webseiten

von Peter Marksteiner (Ausgabe 99/1, März 1999)

 

1) CGI und Dynamic HTML

Das WorldWideWeb ist längst nicht mehr nur eine Sammlung von Dokumenten, die auf Servern in aller Welt bereitliegen und von dort mittels eines Klienten (Browsers) abgeholt werden können. Vielmehr können viele Webserver ihre Dokumente nach den Wünschen der Kunden individuell gestalten. Ein typisches Beispiel dafür sind Suchmaschinen wie AltaVista (http://www.altavista.com): Mittels eines Web-Formulars schicken die Klienten die gewünschten Suchbegriffe an den Server, und dort wird für jeden Suchauftrag in Sekundenbruchteilen ein eigenes HTML-Dokument generiert, das die Ergebnisse der Datenbankabfrage enthält.

Die weitestverbreitete Methode zum Erzeugen solcher dynamischer1) HTML-Seiten ist CGI, das Common Gateway Interface. Wie bei statischen HTML-Dokumenten kommunizieren Klient und Server nach wohldefinierten Regeln, dem Hypertext Transfer Protocol (HTTP); daneben ist aber noch ein dritter Partner beteiligt: Der Server beauftragt über das Common Gateway Interface ein externes Programm, das gewünschte Dokument zu erstellen. Ein solches externes Programm wird CGI-Programm oder meistens CGI-Skript genannt (der Unterschied zwischen einem Programm und einem Skript ist Gegenstand endloser philosophischer Debatten, auf die ich mich hier nicht einlassen will).

CGI definiert nur die Schnittstelle zwischen dem Webserver und dem externen Programm, also die Regeln, nach denen die Daten ausgetauscht werden. Solange diese Regeln eingehalten werden, kann das externe Programm ansonsten tun und lassen, was es will. Es gibt keine Vorschriften, in welcher Sprache CGI-Skripts zu schreiben sind, und neben dem Erstellen von HTML-Dokumenten können CGI-Skripts noch vieles andere tun: Beispielsweise einen Kommentar ins Online-Vorlesungsverzeichnis der Uni Wien (http://www.univie.ac.at/UNI-Daten/kommentar.html) eintragen. Oder bei einem Online-Versandhaus ein Buch bestellen. Oder den Rechner, auf dem der Webserver läuft, abschalten. Häufig sind diese "Nebenwirkungen" der eigentliche Hauptzweck: Ein großer Teil des immer mehr blühenden electronic commerce beruht auf CGI-Applikationen. Auch das letzte Beispiel ist nicht so abwegig, wie es vielleicht scheinen mag: Viele Werkzeuge zur Systemadministration können auch über ein Web-Interface bedient werden.

Auf allen Webservern, die das EDV-Zentrum den Instituten, Mitarbeitern und Studierenden der Uni Wien zur Verfügung stellt (www.univie.ac.at, www.unet.univie.ac.at, mailbox.univie.ac.at, rs6000.univie.ac.at), besteht die Möglichkeit, CGI-Skripts auszuführen. Dieser Artikel wendet sich vor allem an Benutzer, die ihre Webseiten auf einem dieser Server durch Dynamic HTML bereichern wollen.

2) CGI oder JavaScript oder ...?

CGI ist bei weitem nicht die einzige Methode, dynamische HTML-Seiten zu generieren oder generell Webseiten mit diversen dynamischen Effekten auszustatten. Eine umfassende Diskussion würde hier zu weit führen (siehe z.B. ftp://ftp.univie.ac.at/archive/faq/www/cgi-faq); ich möchte aber kurz auf JavaScript eingehen. Einerseits ist JavaScript sehr weit verbreitet, andererseits wird oft die Frage gestellt: Wozu überhaupt CGI - kann ich nicht alles besser mit JavaScript machen?

JavaScript (nicht zu verwechseln mit Java - das ist etwas völlig anderes) ist eine Erfindung von Netscape. DerJavaScript-Sourcecode wird in ein HTML-Dokument eingebettet, und JavaScript wird vom Klienten ausgeführt, sobald ein JavaScript-fähiger Browser ein solches Dokument anzeigt. Mit JavaScript kann man z.B. das Aussehen des Browserfensters manipulieren, eigene Fenster öffnen und auf Ereignisse wie Mausklicks, Tastendrucke oder Cursorbewegungen reagieren. CGI-Skripts hingegen werden vom Server ausgeführt, und der Klient bekommt den Sourcecode eines CGI-Skripts nie zu sehen.

Die Frage "CGI oder JavaScript?" ist also meistens sinnlos, weil sich die Einsatzbereiche wenig überlappen: Mit JavaScript kann man keine Datenbanken abfragen, weil JavaScript keinen Zugriff auf Datenbanken am Server hat, und mit CGI kann man keine Lampen zum Leuchten bringen, indem man den Cursor darüberbewegt, weil der Server nichts davon weiß, wo sich der Cursor befindet. Nur manche CGI-Funktionen können durch JavaScript zumindest teilweise ersetzt werden: Wenn z.B. ein CGI-Skript über ein Web-Formular aufgerufen wird, so überprüft das Skript üblicherweise die Angaben im Formular auf Vollständigkeit und Konsistenz. Eine ähnliche Prüfung kann auch am Klienten mittels JavaScript durchgeführt werden, sodaß fehlerhaft ausgefüllte Formulare gar nicht erst zum Server geschickt werden.

Auch sollte man nicht vergessen, daß JavaScript spezielle Unterstützung durch den Browser braucht, während es einem CGI-Skript vollkommen egal ist, mit welchem Klienten die erstellten Dokumente vom Server geholt werden. Der Klient kann normalerweise gar nicht unterscheiden, ob ein HTML-Dokument statisch ist oder mittels CGI dynamisch generiert wurde. Wer keinen JavaScript-fähigen Browser hat oder - wie ich - die JavaScript-Funktion normalerweise abdreht (allein schon wegen der überaus häufigen und lästigen "JavaScript Errors"), hat mit einem Übermaß an JavaScript keine Freude. Aber auch die "friedliche Koexistenz" von CGI und JavaScript ist möglich: Der HTML-Code, der von einem CGI-Skript generiert wird, kann ohne weiteres auch JavaScript enthalten.

Noch eine kurze Bemerkung zu den Microsoft FrontPage Server Extensions: Microsoft FrontPage ist ein Programm zur Erstellung von Webseiten, das im allgemeinen mehr oder weniger standardkonformen HTML-Code erzeugt, der für jeden beliebigen Webserver geeignet ist. Manche Features von solchen Dokumenten sind jedoch nicht standardkonform und funktionieren nur, wenn am Webserver die FrontPage Server Extensions installiert sind. Mit diesen Erweiterungen kann man auch dynamische Applikationen wie Gästebücher erstellen - oft wohl etwas einfacher als mittels CGI. Auf den Webservern des EDV-Zentrums stehen die FrontPage Server Extensions jedoch nicht zur Verfügung: Diese Server enthalten zahlreiche lokale Modifikationen (z.B. Unterstützung für DCE/DFS, siehe Kapitel 5), und es wäre nur mit unverhältnismäßigem Aufwand möglich, diese hochspezialisierten Server mit den FrontPage Server Extensions auszurüsten.

3) Was muß ich wissen?

Es liegt in der Natur der Sache, daß das Erstellen von Programmen, die dynamische HTML-Seiten erzeugen, wesentlich komplexer und aufwendiger ist als das Erstellen von statischen HTML-Dokumenten. Mehr oder minder fundierte Kenntnisse in folgenden Bereichen sind erforderlich:

  • Programmiersprache (Perl)
    Natürlich braucht man solide Kenntnisse der Sprache, in der man CGI-Skripts schreibt, und das ist meistens Perl (siehe Kapitel 4). Auf CGI-Skripts in anderen Sprachen wird in diesem Artikel nicht eingegangen.
  • HTML
    Ohne fundierte HTML-Kenntnisse kann man schwer ein Programm schreiben, das HTML-Code erzeugt. Am EDV-Zentrum ist das Handbuch Publizieren im World Wide Web erhältlich, und es werden dazu Kurse angeboten (siehe http://www.univie.ac.at/EDV-Zentrum/kurse.html#wp1). Verweise auf weiterführende Literatur zu HTML findet man auch im Comment-Artikel HTML mit Stil (http://comment.univie.ac.at/98-2/23/).
  • Betriebssystem (Unix)
    Auch wenn gut geschriebene CGI-Skripts weitgehend portabel sind, hängen dennoch manche Details wie Dateinamen-Konventionen vom Betriebssystem ab. Alle Webserver des EDV-Zentrums laufen auf Unix-Systemen. Um dort CGI-Skripts zu implementieren, sind zumindest bescheidene Unix-Grundkenntnisse erforderlich: Man sollte imstande sein, mittels Telnet ein Login auf dem Unix-Rechner durchzuführen und dort in der Shell einfache Befehle auszuführen (z.B. Kopieren, Verschieben und Löschen von Dateien und Verzeichnissen, Anzeigen und Ändern von Berechtigungen). Anfänger, die sich mit den Text-Editoren unter Unix - vi, emacs, pico - nicht anfreunden wollen, können natürlich CGI-Skripts auf einem Windows-PC editieren und jedesmal mittels FTP übertragen; auf Dauer ist dieses Verfahren aber wohl zu umständlich. Weitere Informationen bietet das Handbuch Einführung in Unix und der gleichnamige Kurs (siehe http://www.univie.ac.at/EDV-Zentrum/kurse.html#ux). Das Wichtigste ist auch in den Unterlagen zu Unix for Unet (http://www.univie.ac.at/EDV-Zentrum/tutorials.html) kurz zusammengefaßt.
  • CGI und HTTP
    Paradoxerweise braucht man sich um die Details der CGI-Schnittstelle im allgemeinen wenig zu kümmern: Das CGI-Modul (siehe Beispiel 3 in Kapitel 6) besorgt das weitgehend automatisch. Die vollständigen CGI-Spezifikationen findet man unter http://hoohoo.ncsa.uiuc.edu/cgi/. Auch vom HTTP-Protokoll muß man selten mehr wissen, als in den Kapiteln 6 und 7 beschrieben ist. Das HTTP-Protokoll wird in RFC1945 (Version 1.0) und RFC2068 (Version 1.1) definiert; beide sind unter http://ftp.univie.ac.at/netinfo/rfc/ zu finden.

Auf den ersten Blick mag diese lange Liste von Erfordernissen abschreckend wirken und den Eindruck erwecken, daß CGI nur etwas für Gurus und Experten sei. In vielen Fällen kann man sich jedoch darauf beschränken, bestehende CGI-Skripts zu installieren, wobei meistens nur leichte Anpassungen an die lokalen Gegebenheiten erforderlich sind. Das ist natürlich viel leichter und weniger aufwendig, als eine eigene CGI-Applikation von Anfang an zu entwickeln. Für fast alle gängigen CGI-Anwendungen (guest books, bulletin boards, mailing lists usw.) sind fertige Lösungen zu finden. Im WWW gibt es etliche umfangreiche Archive mit großen Mengen an CGI-Skripts unterschiedlicher Qualität; ein guter Ausgangspunkt für die Suche ist http://www.cgi-resources.com/.

4) Die Programmiersprache Perl

Wie bereits erwähnt, können CGI-Skripts in (fast) jeder beliebigen Programmiersprache geschrieben werden. Mit Abstand die beliebteste und weitestverbreitete Sprache für CGI-Applikationen ist jedoch Perl. Das hat dazu geführt, daß CGI und Perl vielfach miteinander verwechselt oder gar für Synonyme gehalten werden. Eine Schnittstellen-Definition ist aber natürlich etwas anderes als eine Programmiersprache, und es ist Unsinn, ein Perl-Programm, das absolut nichts mit dem WWW zu tun hat, als CGI-Skript zu bezeichnen. Wer in der Newsgruppe comp.lang.perl.misc eine Frage zu CGI stellt, wird - mehr oder minder höflich - darauf aufmerksam gemacht, daß das mit Perl nichts zu tun habe, und auf comp.infosystems.www.authoring.cgi verwiesen.

Perl ist eine sehr mächtige Sprache, die für alles mögliche - von Systemadministration bis zu Buchhaltung - eingesetzt werden kann und auch eingesetzt wird. Unter anderem haben folgende Eigenschaften dazu beigetragen, daß Perl zur führenden Sprache für Web-Applikationen geworden ist:

  • Eines der wichtigsten Anwendungsgebiete von Perl, für die die Sprache ursprünglich entwickelt wurde, ist die Bearbeitung und Formatierung von Text-Dateien. Der einzige Datentyp in Perl ist die Zeichenkette (character string). Mit den sogenannten regular expressions steht ein sehr mächtiges Werkzeug zur Verfügung, mit dem man auch komplexe Manipulationen von Zeichenketten sehr elegant und kompakt - und für Uneingeweihte unverständlich - formulieren kann. Nachdem HTML-Dokumente nichts anderes sind als eine spezielle Form von Zeichenketten bzw. Text-Dateien, ist Perl ideal für Programme geeignet, die solche Dokumente erzeugen.
  • Perl wird interpretiert2), d.h. es ist nicht nötig, ein Perl-Programm vor der Ausführung in einem eigenen Schritt zu kompilieren. Das erhöht die Produktivität bei der Programmentwicklung.
  • Perl ist portabel: Obwohl Perl ursprünglich für Unix-Systeme entwickelt wurde und seine Herkunft aus der Unix-Welt nicht verleugnen kann, gibt es neben zahlreichen Unix-Versionen auch Implementationen für VMS, MS-Windows, Macintosh und andere.
  • Perl ist Freeware und kann von zahlreichen FTP-Servern in aller Welt bezogen werden, z.B. von http://ftp.univie.ac.at/packages/perl/.
  • Die Softwarequalität von Perl hat sicher wesentlich zum Erfolg beigetragen. Es gibt wohl nur wenige Softwareprodukte, die bei so hoher Komplexität so wenige Fehler haben - und das auf vielen verschiedenen Plattformen.
  • Die Funktionalität von Perl kann durch Module erweitert werden: Im Comprehensive Perl Archive Network (CPAN) gibt es Module für die verschiedensten Zwecke, z.B. Systemadministration, Textverarbeitung, Mathematik und vieles andere. Für CGI-Applikationen ist natürlich das CGI-Modul das wichtigste (siehe Kapitel 6), aber auch diverse Module zu HTML, HTTP, FTP und anderen Netzwerkprotokollen können nützlich sein.

Natürlich hat Perl auch Nachteile: Perl-Programme müssen jedesmal interpretiert werden und sind daher meistens weniger effizient als z.B. äquivalente C-Programme. Obwohl die Anfangsgründe von Perl relativ leicht zu lernen sind, gibt es eine Unmenge von Spezialfällen und Ausnahmen, sodaß selbst versierte Perl-Programmierer immer wieder auf Überraschungen stoßen. Wie in jeder Sprache kann man in Perl klare, übersichtliche und leicht lesbare Programme schreiben; Perl macht es aber auch sehr leicht, absolut unleserlichen und kryptischen Code zu produzieren.

Markenzeichen von Perl ist das Kamel, nach dem Titelbild des Standardwerks Programming Perl von Larry Wall, dem Schöpfer von Perl. Überhaupt ist in der Perl-Literatur und Dokumentation viel von Kamelen die Rede. Neben Programming Perl gibt es noch Learning Perl ("Lama", für Anfänger besser geeignet), Perl Cookbook ("Widder"), Advanced Perl Programming ("Panther") und Learning Perl on Win32 Systems ("Gecko"), alle bei O'Reilly erschienen (siehe http://www.oreilly.com/). Auch ein großer Teil von Mastering Regular Expressions ("Eulen") ist Perl gewidmet. Einige dieser Bücher sind auch in deutscher Übersetzung erhältlich. Weitere Literaturhinweise und Informationen aller Art zu Perl findet man unter http://www.perl.com/.

Ein wichtiger Bestandteil jeder Perl-Installation ist die Online-Dokumentation, die, vollständig ausgedruckt, mehrere tausend Seiten umfassen würde. Zum Lesen der Dokumentation dient der Befehl perldoc. Auch im WWW ist sie an vielen Stellen zu finden, z.B. unter http://ftp.univie.ac.at/packages/perl/doc/manual/html/.

5) CGI-Unterstützung auf den Webservern der Uni Wien

Wenn man auf irgendeinem Webserver CGI-Skripts ausführen will, so muß man sich vorher über die lokalen Gegebenheiten informieren. Für die Webserver des EDV-Zentrums (www.univie.ac.at, www.unet.univie.ac.at, mailbox.univie.ac.at, rs6000.univie.ac.at) gilt folgendes:

  • CGI-Skripts sind generell ohne besondere Einschränkungen erlaubt und können sich in beliebigen Verzeichnissen befinden. Es wird aber trotzdem empfohlen, alle CGI-Skripts in einem Unterverzeichnis namens cgi-bin abzulegen (z.B. auf dem Unet- oder Mailboxrechner in $HOME/html/cgi-bin/).
  • Der Filename muß unbedingt auf .cgi enden, sonst wird ein CGI-Skript nicht als solches erkannt. Der Server führt es dann nicht aus, sondern schickt den Sourcecode an den Klienten.
  • Auf allen Webservern des EDV-Zentrums steht eine aktuelle Version von Perl (5.004 oder 5.005) mit zahlreichen Modulen zur Verfügung. Perl kann unterschiedslos als /bin/perl, /usr/bin/perl oder /usr/local/bin/perl aufgerufen werden.
  • Die meisten Webserver laufen mit einer Benutzungsberechtigung, die aus Sicherheitsgründen möglichst wenig Privilegien hat (z.B. als Benutzer "nobody"). Häufig werden auch alle CGI-Skripts mit den Rechten und Privilegien dieses Benutzers ausgeführt. Auf den Webservern der Uni Wien hingegen laufen alle CGI-Skripts mit den Rechten des Eigentümers ("owner") des Skripts. Das hat z.B. den Vorteil, daß ohne weiteres Dateien im home directory gelesen und geschrieben werden können. Welche Auswirkungen das auf die Sicherheit hat, ist in Kapitel 9 beschrieben.
  • Am Unet-Rechner (demnächst auch am Mailbox-Rechner) befinden sich alle HTML-Dokumente und damit auch alle CGI-Skripts in einem Distributed File System (DFS). Damit CGI-Skripts auch dort wie gewohnt funktionieren, waren spezielle Anpassungen des Webservers notwendig: CGI-Skripts auf dem Unet-Webserver benötigen ein keytab file. Beim ersten Aufruf eines CGI-Skripts erhält man ausführliche Instruktionen, wie man ein solches anlegt (siehe dazu auch http://www.unet.univie.ac.at/info/faq.html#cgi).
  • Es ist nicht immer leicht herauszufinden, welche Berechtigungen CGI-Skripts und HTML-Dokumente generell brauchen. Einerseits sind ausreichende Berechtigungen erforderlich, damit der Webserver (der, wie erwähnt, mit wenig Privilegien läuft) die Dokumente lesen bzw. die Skripts ausführen kann. Andererseits sind zu hohe Privilegien zum Schutz vor Zugriff durch Unbefugte nicht erwünscht und manchmal aus Sicherheitsgründen nicht erlaubt (z.B. dürfen CGI-Skripts nicht world writable sein). Auf den Webservern des EDV-Zentrums3) steht die Prozedur html-perm zur Verfügung, die automatisch die erforderlichen Berechtigungen setzt. Mit
         html-perm -all
    werden rekursiv die Berechtigungen für den gesamten Inhalt des html-Verzeichnisses gesetzt. Man kann auch einzelne Dateien als Argument angeben ? z.B.:
         html-perm index.html
    html-perm cgi-bin/*.cgi
  • Auch server-side includes sind generell möglich. Zeilen wie
         <!-- #include virtual="myscript.cgi"-->
    können in HTML-Dokumente eingebettet werden, sodaß ein Teil des Dokuments dynamisch generiert wird. Weitere Informationen zu server-side includes sind in der Apache-Dokumentation (http://gd.tuwien.ac.at/infosys/servers/http/apache/) zu finden. Es ist übrigens nicht notwendig, solche Dokumente mit der Extension .shmtl (server-parsed HTML) abzuspeichern, .hmtl genügt.

Wir bitten um Verständnis dafür, daß das Service- und Beratungszentrum bei CGI-Problemen nur minimale Unterstützung bieten kann.

6) Einfache Beispiele

Eine Sammlung von einfachen Beispielen, die die Grundprinzipien der CGI-Programmierung illustrieren, ist unter http://www.univie.ac.at/cgi-demo/ zu finden. Dort gibt es zu jedem Beispiel den kompletten Sourcecode und ausführliche Erläuterungen, und es werden verschiedene Methoden des Aufrufs von CGI-Skripts diskutiert. Im folgenden wird nur das Wichtigste kurz zusammengefaßt.

Beispiel 1: Ein einfaches Shell-Skript

#!/bin/sh
echo "Content-type: text/plain"
echo ""
env

Das erste echo liefert den HTTP Header, wie er vom HTTP-Protokoll verlangt wird. In Wirklichkeit ist das nur ein Teil des Headers, der zum Klienten geschickt wird; der Rest wird vom Server automatisch generiert. Der Header und der Inhalt des Dokuments sind immer durch eine Leerzeile getrennt. Der Inhalt ist hier der Output des Unix-Befehls env, der eine Liste aller Umgebungsvariablen liefert. Manche dieser Variablen sind für die Kommunikation zwischen Klient, Server und CGI-Applikation von Bedeutung: In Beispiel 3 werden davon REMOTE_HOST und REMOTE_ADDR verwendet. Mehr über Umgebungsvariablen ist auf der oben erwähnten Webseite zu finden. Nachdem es sich um ein Dokument in plain text handelt, ist kein HTML-Code und keine Formatierungs-Information notwendig.

Beispiel 2: Ein einfaches Perl-Skript

#!/bin/perl -w
my $now = scalar localtime;
print <<EOF;
Content-type: text/html

<HTML>
<HEAD><TITLE>Datum und
Uhrzeit</TITLE></HEAD>
<BODY>
<H1>Datum und Uhrzeit:</H1>
<H2>$now</H2>
</BODY></HTML>
EOF

Analog zum vorigen Beispiel produziert auch dieses Skript den Header, dann eine Leerzeile und dann den Inhalt des Dokuments. Ein Großteil des HTML-Codes ist statisch, nur die Variable $now wird jedes mal neu berechnet und enthält bei jedem Aufruf die aktuelle Uhrzeit am Server.

Beispiel 3: Ein Perl-Skript mit Parametern

Das Original (http://www.univie.ac.at/cgi-demo/cgi-bin/param.cgi) ist deutlich umfangreicher; hier wird nur eine stark vereinfachte (aber funktionsfähige) Version gebracht. Das Skript kann mit beliebigen Parametern aufgerufen werden und produziert eine formatierte Liste mit Namen und Wert aller Parameter. Das vollständige Skript tut noch einiges mehr: Wird es mittels POST aufgerufen (siehe Kapitel 7), so werden einige Parameter in eine Datei geschrieben. Beim nächsten Aufruf wird dann der Inhalt dieser Datei angezeigt.

#!/bin/perl -w

use strict;
use CGI qw(:standard);
my $client = $ENV{REMOTE_HOST} ||
$ENV{REMOTE_ADDR} ||
"Unbekannter";

my $list = get_parameter_list();
print header,
start_html('CGI-Demo: Parameter'),
h1({-align=>"CENTER"}, 'CGI-Demo: Parameter'),
h2({-align=>"CENTER"}, "Willkommen, $client!"),
$list,
end_html;

sub get_parameter_list()
{
my @names = param;
return "" unless scalar @names;
my $list = "<DL>\n";
foreach ( @names )
{
$list .= "<DT>$_<DD>" . param($_) . "\n";
}
return $list . "</DL>\n";
}

Dieses Beispiel illustriert vor allem die Funktionsweise des CGI-Moduls. Zu Beginn wird die Routine get_parameter_list aufgerufen, die die Funktion param des CGI-Moduls verwendet: Ohne Argumente liefert param eine Liste der Namen aller Parameter; den Wert des Parameters x erhält man mit param('x').

Danach wird eine Reihe von HTML-Elementen mit Hilfe von Funktionen des CGI-Moduls erzeugt, z.B. der HTTP-Header mittels header, der Beginn des Dokuments mit <HEAD> und <TITLE> mittels start_html usw. Auf den ersten Blick mag der Wert der Funktionen zum Generieren von HTML-Elementen zweifelhaft erscheinen ? schließlich macht es keinen großen Unterschied, ob man

print h1("CGI-Demo");

oder

print "<H1>CGI-Demo</H1>";

schreibt. Bei komplexeren Objekten wie Tabellen sind diese Funktionen jedoch oft viel einfacher und weniger fehleranfällig als expliziter HTML-Code. Die wahre Stärke des CGI-Moduls zeigt sich bei HTML-Formularen: Besonders elegant kann man damit Skripts schreiben, die sich selber aufrufen und alle Parameter von einem Aufruf zum nächsten weitergeben. http://www.univie.ac.at/cgi-demo/cgi-bin/darts.cgi ist ein Beispiel eines solchen Skripts.

In diesem Beispiel wird nur ein kleiner Teil der zahlreichen Funktionen des außerordentlich mächtigen und umfangreichen CGI-Moduls verwendet. Mehr über das CGI-Modul findet man in der Online-Dokumentation, die mit dem Befehl perldoc CGI gelesen werden kann, bzw. im WWW unter http://www.univie.ac.at/cgi-demo/cgi-bin/showman.cgi?command=CGI. Empfehlenswert ist auch das Buch Official Guide to Programming with CGI.pm von Lincoln Stein, dem Autor des CGI-Moduls.

7) GET oder POST?

Das HTTP-Protokoll sieht mehrere Methoden vor, mit denen Klienten Anfragen an den Server richten können. Für CGI-Skripts sind zwei davon relevant, nämlich GET und POST. Wird ein Skript explizit aufgerufen, z.B als http://www.univie.ac.at/cgi-demo/cgi-bin/param.cgi?e=mc2, so wird automatisch die GET-Methode verwendet. Bei Aufruf über ein HTML-Formular kann man sich die Methode aussuchen ? z.B. <FORM ACTION="cgi-bin/param.cgi" METHOD="POST">

Was sind nun die Unterschiede zwischen diesen beiden Methoden sowie deren Vor- und Nachteile?

  • Die Übergabe der Parameter vom HTTP-Server an das CGI-Programm erfolgt auf unterschiedliche Weise. Wer das CGI-Modul verwendet, braucht sich allerdings nicht darum zu kümmern - z.B.
    my $name = param('nachname');
    funktioniert mit beiden Methoden. Nur wer darauf besteht, den Input selbst zu dekodieren, muß sich in die CGI-Spezifikationen vertiefen und Code für beide Methoden schreiben.
  • Bei GET-Abfragen sind die Parameter Bestandteil des URL. Es ist daher möglich, Bookmarks (Lesezeichen) auf bestimmte Datenbankabfragen zu setzen.
  • Unter Umständen ist die Länge der Parameter-Liste bei GET-Abfragen beschränkt. Größere Datenmengen sollte man sicherheitshalber mittels POST zum Server schicken.
  • GET-Abfragen sollen nichts am Zustand des Servers ändern, auch wenn sie beliebig oft wiederholt werden. Diese Eigenschaft wird manchmal als Idempotenz bezeichnet. Alle CGI-Skripts mit "Nebenwirkungen" (Datenbank-Eintragungen, Online-Bestellungen und -Anmeldungen usw.) sollten daher unbedingt mittels POST aufgerufen werden; die Skripts sollten auch überprüfen, ob die richtige Methode verwendet wird. Wenn beispielsweise eine Online-Bestellung mittels GET durchgeführt wird, kann es viel zu leicht passieren, daß sie unabsichtlich wiederholt wird, z.B. mit der Zurück-Funktion des Browsers.

Im Zweifelsfalle sollte man lieber POST verwenden. Formulare, die mit Hilfe des CGI-Moduls generiert werden, verwenden ebenfalls POST, sofern man die Methode nicht explizit angibt.

8) "500 Internal Server Error"

Mit an Sicherheit grenzender Wahrscheinlichkeit wird ein CGI-Skript beim ersten Aufruf nicht das gewünschte Ergebnis liefern, sondern die lakonische Meldung 500 Internal Server Error. Wie kann man nun die Fehler möglichst rasch finden und beheben?

  • Unbedingt das Skript interaktiv testen, bevor es über einen Browser aufgerufen wird - z.B.:
         cd $HOME/html/cgi-bin
    ./myscript.cgi
    Zumindest Syntaxfehler wie vergessene Strichpunkte oder Anführungszeichen findet man damit sofort. Skripts, die das CGI-Modul verwenden - und es gibt selten einen triftigen Grund, es nicht zu verwenden - verhalten sich interaktiv fast genauso wie bei Aufruf über den Webserver, sodaß man sie interaktiv sehr gründlich testen kann.
  • Falsche Berechtigungen zählen zu den häufigsten Fehlerquellen (siehe dazu Kapitel 5).
  • Perl unbedingt mit der Option -w (enable warnings) aufrufen! Es empfiehlt sich auch use strict, das fördert die Disziplin und erzwingt eine wenig fehleranfällige Programmierweise, weil dann Schlampereien wie nicht deklarierte Variablen nicht erlaubt sind. Zur Fehlersuche ist auch use diagnostics nützlich. Selbstverständlich sollte man alle Fehlermeldungen und Warnungen auch ernst nehmen. Im Idealfall produziert ein Skript überhaupt keine Warnungen. Meldungen wie z.B. Use of uninitialized variable at line 37 sollte man auf keinen Fall ignorieren, auch wenn das Skript ansonsten einwandfrei funktioniert: Nur wenn man genau versteht, warum solche Warnungen kommen, und wenn man sicher ist, daß sie harmlose Ursachen haben, sind solche Meldungen zu tolerieren.
  • Der Webserver schreibt alle Fehlermeldungen (stderr) in ein Logfile, auf das nicht privilegierte Benutzer keinen Zugriff haben. Mit Hilfe des CGI::Carp-Moduls kann man die Fehlermeldungen auf eine beliebige Datei umleiten:
         BEGIN
    {
    use CGI::Carp "carpout";
    my $log = "/my/home/logs/cgi.log";
    open LOG, ">>$log"
    or die "Cannot open $log: $!";
    carpout *LOG
    if defined $ENV{"GATEWAY_INTERFACE"};
    }
  • Eine häufige Fehlerquelle sind DOS newlines. Bei DOS- und MS-Windows-Dateien beginnt eine neue Zeile mit zwei Zeichen, carriage return (<Control>M, ^M) und linefeed (^J), während bei Unix-Dateien nur ein linefeed zwei Zeilen trennt. Eine HTML-Datei mit DOS newlines ist auch unter Unix kein Problem, die zusätzlichen carriage returns werden ignoriert. Bei CGI-Skripts sind sie jedoch fatal: Die erste Zeile eines Skripts wird dann z.B. als
         #!/bin/perl^M
    interpretiert, und es wird vergebens nach einem Programm namens /bin/perl^M gesucht. Daher unbedingt CGI-Skripts mit FTP im ASCII-mode (nicht binary!) übertragen, wenn man sie auf einem Windows-PC editiert! Es gibt auch Windows-Editoren (z.B. PFE, den Programmer's Free Editor, zu finden unter http://www.univie.ac.at/simtel.net/), die imstande sind, Dateien im Unix-Format abzuspeichern. Auf Unix-Rechnern kann man DOS newlines im vi mit dem Befehl :%s/^V^M$// entfernen.
  • Ein Grundsatz, der in der Perl-Literatur unzählige Male wiederholt wird, lautet: Always check the return code of system calls. System calls (das sind vor allem externe Programme und alle Funktionen, die mit Input/Output und File-Manipulationen zu tun haben) liefern einen return code, der aussagt, ob die Operation erfolgreich durchgeführt wurde. Befehle wie
         open GUEST, ">>guestbook";
    print GUEST $guestbook_entry;
    close GUEST;
    können aus verschiedensten Gründen schiefgehen: Die Berechtigungen der Datei erlauben die Operation nicht; der Befehl wird von einem falschen Verzeichnis aus aufgerufen, wo es diese Datei gar nicht gibt; beim Schreiben auf eine Datei wird die disk quota überschritten usw. Wenn man sich blind darauf verläßt, daß solche Operationen funktionieren, kann man sich sehr leicht Fehler einhandeln, die schwer zu finden sind - besonders, wenn sie nur intermittierend auftreten. Daher besser:
         my $guest = "/my/home/data/guestbook";
    open GUEST, ">>$guest" or die
    "Cannot open $guest: $!";
    print GUEST $guestbook_entry;
    close GUEST or die
    "Cannot close $guest: $!";
    Die Fehlermeldungen enthalten alle wichtigen Informationen, die man braucht, um den Fehler zu finden - vor allem den vollen Pfadnamen der Datei und die Meldungen, die der jeweilige system call liefert (diese stehen in der Variablen $!). Pedanten würden auch den return code des print-Befehls überprüfen; dies zu unterlassen, ist aber eine läßliche Sünde.

9) Sicherheit

Leider gibt es auf die Frage "Sind CGI-Skripts ein Sicherheitsrisiko?" nur eine Antwort: "Ja" - ohne wenn und aber. Mit einem CGI-Skript erlaubt man der ganzen Welt, ein Programm auf dem Webserver auszuführen. Natürlich erfüllt dieses Programm eine wohldefinierte Aufgabe und sollte daher nichts Böses anrichten können. Mit allen möglichen Tricks kann man aber viele Programme dazu bringen, Dinge zu tun, an die deren Autoren nicht im mindesten gedacht haben - oft mit katastrophalen Konsequenzen.

Das heißt aber nicht, daß man deswegen in Panik verfallen und vor lauter Paranoia auf CGI-Skripts gänzlich verzichten sollte: Auch die Straße zu überqueren ist ein Sicherheitsrisiko. Die meisten Leute bleiben trotzdem nicht den ganzen Tag zu Hause, sondern geben lieber acht, nicht überfahren zu werden. Was kann man nun tun, um das Risiko möglichst gering zu halten?

  • Wer CGI-Skripts verwendet, sollte unbedingt vorher die WWW Security Frequently Asked Questions lesen (http://www.w3.org/Security/Faq/). Ein großer Teil davon ist nur für Leute relevant, die selbst Webserver betreiben; man findet aber auch ein umfangreiches Kapitel über CGI, das wesentlich detaillierter auf alle Sicherheitsaspekte der CGI-Programmierung eingeht, als es im vorliegenden Artikel möglich ist. Auch Kapitel 19 des bereits erwähnten Perl Cookbook von Tom Christiansen, das CGI gewidmet ist, enthält eine fundierte Diskussion von Sicherheitsfragen.
  • Der wichtigste Grundsatz ist wohl: Immer die Parameter, die an ein CGI-Skript übergeben werden, überprüfen, und sich nie darauf verlassen, daß das Skript auf eine bestimmte Art aufgerufen wird. Auch wenn die Eingabe vorher mit JavaScript überprüft wird (siehe Kapitel 2) - JavaScript kann man abdrehen. Auch wenn die richtigen Parameter in einem Formular vordefiniert sind - das Formular kann man kopieren und die Kopie modifizieren.
  • Besondere Vorsicht ist bei allen Parametern geboten, die an externe Programme (Shells) übergeben werden. Ein typisches Beispiel sind Mailadressen: Ein Mensch mit bösen Absichten kann in einem Webformular eine Pseudo-Mailadresse angeben, die - an der richtigen Stelle - die Zeichenkette "/bin/rm -Rf /" enthält. Solange ein solcher Wert nur in Perl verarbeitet wird (beispielsweise als Variable $email), kann nichts Schlimmes passieren:4) Für Perl ist das eine Zeichenkette wie jede andere auch. Wenn ein CGI-Skript diese Adresse aber ungeprüft an das Mail-Programm weitergibt:
            system("/bin/mail $email < $message");
    so muß der Webmaster mit unangenehmen Überraschungen rechnen: Mit diesem Befehl wird dann die gesamte Festplatte gelöscht! Ob wirklich das Schlimmste eintritt, hängt von der Konfiguration des Webservers ab; bei gut konfigurierten Servern hält sich der Schaden in Grenzen. Auf den Webservern des EDV-Zentrums der Universität Wien laufen CGI-Skripts mit den Privilegien des Eigentümers (siehe Kapitel 5); in diesem Fall können "nur" Dateien gelöscht werden, die diesem Eigentümer gehören - aber auch das ist wohl schlimm genug. Daher unbedingt die Parameter auf shell metacharacters überprüfen! Details dazu findet man in der oben angegebenen Literatur.
  • Auch die im vorigen Kapitel beschriebenen Techniken zur Vermeidung von Programmfehlern erhöhen die Sicherheit: Es leuchtet ein, daß ein Programm, das generell weniger fehleranfällig ist, auch weniger zu Sicherheitsproblemen neigt.
  • Besonders bei CGI-Skripts, die man von FTP-Servern, Websites, guten Freunden oder sonstwoher bezieht, sollte man auf Sicherheit achten. Nach folgenden Kriterien kann man solche Skripts beurteilen: Stammt das Skript aus einer renommierten Quelle? Wird es von Experten empfohlen (z.B in einschlägigen Newsgruppen wie comp.infosystems.www.authoring.cgi)? Ist der Sourcecode übersichtlich und lesbar, auch für Laien mit bescheidenen Programmierkenntnissen? Verwendet es das CGI-Modul, oder versucht es, den Input selbst zu dekodieren? Verwendet es sichere und wenig fehleranfällige Programmiertechniken, wie sie in diesem und im vorigen Kapitel beschrieben werden?

Zum Abschluß noch ein Grundsatz, den man beim Programmieren überhaupt beherzigen sollte: Don't reinvent the wheel. Die Wahrscheinlichkeit, daß noch niemand auf der Welt eine ähnliche Aufgabe zu lösen hatte, ist bei den meisten CGI-Applikationen sehr gering. Auch wenn sich eine geeignete Komplettlösung für ein konkretes Problem nicht finden lassen sollte, so können die meisten Aufgaben doch aus einigen Einzelbausteinen zusammengesetzt werden, für die es fertige Programme oder Programmteile gibt. Vor allem das CGI-Modul ist ein Baukasten mit einem umfangreichen Satz an solchen Bausteinen. Natürlich soll sich niemand dadurch von schöpferischer Tätigkeit abhalten lassen: Oft ist es besser und einfacher, ein Skript selbst zu schreiben, als zu versuchen, ein unlesbares Programm aus einer dubiosen Quelle zu verstehen und zu implementieren. Die besten Ergebnisse wird man erzielen, wenn man die eigene Kreativität mit der Summe des Wissens der internet community kombiniert.


 

1) In diesem Artikel dient die Bezeichnung Dynamic HTML nur zur Unterscheidung von statischen, d.h. mehr oder minder unveränderlichen HTML-Dokumenten. Es gibt aber keine allgemeingültige Definition - der Begriff wird für alles mögliche (vor allem zu Marketingzwecken) gebraucht und wohl auch mißbraucht.

2) In Wirklichkeit ist die Sache etwas komplizierter - Perl ist eine Art Mittelding zwischen kompilierten und interpretierten Sprachen.

3) Auf dem Server www.unet.univie.ac.at ist kein Login möglich, die Prozedur kann jedoch auf unet.univie.ac.at durchgeführt werden.

4) Mit einigen Ausnahmen, z.B. bei Verwendung von eval.