CGI / Perl: Bevor Sie anfangen...
Völlige Freiheit – und genügend Raum für Probleme
In der Basiskonfiguration läßt Perl einem jede Menge Freiheiten. So kann man
wie zu besten BASIC-Zeiten Variablen bei Bedarf benutzen und ihnen einen Wert
zuweisen, ohne daß Perl anfangs um die Variable weiß. Man könnte annehmen, daß
das kein Problem darstellt, da man ja ohnehin nur Variablen verwendet, die man
vorher irgendwo definiert hatte, aber was ist, wenn man sich irgendwo
vertippt?
Das Ergebnis sind dann schwer zu ermittelnde Fehler, die einen bald in den
Wahnsinn treiben. So kann es passieren, daß eine Bedingung, von der man weiß,
daß sie unter bestimmten Voraussetzungen erfüllt ist, nie auslöst – oder in der
Umkehrung ständig auslöst, obwohl die Bedingungen gar nicht gegeben sein
dürften.
Wenn Ihr Skript diese Symptome zeigt, dann haben Sie sich garantiert irgendwo
vertippt, sei es bei der Definition der Variablen, sei es irgendwo später bei
der fraglichen Abfrage.
Folgender Perlcode soll das Problem verdeutlichen:
Hier wird eine Variable definiert, deren Inhalt anschließend ausgegeben werden soll. Die Zeile, in der die Ausgabe stattfinden soll, enthält jedoch einen Tippfehler: Zum Einen ist die hier verwendete Variable gar nicht definiert, und andererseits enthält eine nicht definierte Variable implizit den Wert undef, der hier ausgegeben werden soll. Beides sind Dinge, bei denen Perl in der Basis überhaupt nicht mosern wird – mit der Konsequenz, daß Fehler, die in anderen Programmiersprachen den Compiler oder Interpreter meckern lassen, hier zu vollkommen ungeahnten Nebeneffekten führen.
Andere Probleme, die sich auftun können, sind Variable mit undefiniertem
Inhalt, die dann bei Anwendung zu Fehlern führen, da notwendige Daten einfach
nicht zur Verfügung stehen. Stattdessen wundert man sich höchstens ob des
Ergebnisses, das das Skript zurückliefert.
Eine dritte Quelle sind immer Benutzereingaben. Da man es sich im Regelfalle
einfach machen möchte und somit schon mal auf wichtige Überprüfungen
verzichtet, kann es vorkommen, daß Benutzereingaben einfach ungeprüft
übernommen werden – weshalb ein böswilliger Benutzer dem Skript so Eingaben
übergeben kann, die es dazu veranlassen, etwas Unvorhergesehenes zu tun, das es
an sich nie hätte tun sollen.
Eben diese mangelnden Überprüfungen sind die Schwachstelle Nummer 1, wenn es um
Benutzereingaben geht. Somit könnte eine Eingabe, die z. B. für die Abfrage
einer MySQL-Datenbank dient, so aufgebaut sein, daß weitere MySQL-Befehle ans
Skript übergeben werden, die dann von diesem ausgeführt werden. Der Betreiber
des betroffenen Servers wundert sich dann höchstens, warum die Datenbank
zerstört wurde oder – schlimmer noch – vertrauliche Daten entwendet worden
sind und wildfremde Personen Zugriff auf Bereiche des Servers bekommen, auf
denen sie überhaupt nichts zu suchen haben.
Allerdings bietet Perl einem genügend Möglichkeiten, um den Großteil der
Probleme sehr elegant zu vermeiden.
Potentielle Probleme aufdecken
Wenn Sie ein Perlskript schreiben, so werden Sie es im Regelfalle mit
#!/usr/bin/perl einleiten (suchen Sie ggf. mittels
where perl nach dem Perlinterpreter, falls die
Pfadangabe von diesem Beispiel abweicht, und tragen Sie den so ermittelten Pfad
stattdessen ein). Dadurch weiß das System beim Aufruf des Skripts, daß es ein
weiteres Programm zu Hilfe nehmen muß, das das Skript passend übersetzt, so daß
Sie sich den expliziten Aufruf von Perl sparen können.
In seiner Grundeinstellung ist Perl jedoch recht wortkarg, und außer bei
offensichtlichen Fehlern bei der Übersetzung oder der Ausführung des Skripts
werden Sie nur wenig bis gar keine Rückmeldung bekommen, wenn irgendetwas nicht
stimmt, aber das Problem nicht schwerwiegend genug ist, als daß Perl es
abbricht. Um hier mehr zu erfahren, schalten Sie auf jeden Fall Perls Warnungen
ein. Dies geschieht, indem Sie den Aufruf des Perlinterpreters so ergänzen, daß
er wie folgt aussieht: #!/usr/bin/perl -w.
Wenn Perl jetzt über eine potentiell problematische Stelle stolpert, so wird es
eine Warnung ausgeben, selbst wenn das Skript nicht abgebrochen wird. Dennoch
erhalten Sie so einen Hinweis, was schiefläuft, weshalb scheinbar unerklärliche
Proleme auf einmal gar nicht mehr so unerklärlich sind.
Wandelt man obiges Beispiel jetzt entsprechend ab, erhält man folgenden Code:
Führt man diesen Code jetzt aus, so gibt Perl dies aus:
Aha! Hier wird Perl schon deutlich mitteilsamer: So erfahren wir, daß Perl zwei
Variablen erkannt hat ($test und
$tset), wovon beide jeweils nur einmal eingesetzt
werden und zudem mit $tset ein nicht definierter Wert
ausgegeben werden soll.
Dies liefert einem schon deutliche Anhaltspunkte, daß etwas nicht in Ordnung
ist und wo man nach Fehlern suchen muß. Einziger Schönheitsfehler: Das Skript
wird so immer noch ausgeführt, mit allen unerwünschten Nebeneffekten.
Falsch geschriebenen Variablennamen vorbeugen
Dies ist die andere große Fehlerquelle in einem Perlskript. Ist es noch
vergleichsweise kurz, so lassen sich diese Schreibfehler meist sehr schnell
entdecken, nur wenn das Skript länger und auch komplexer wird, so sieht diese
Sache ganz anders aus: Hier hilft meist nur die konventionelle Methode, sprich
das Skrpt auszudrucken und so komplett „zu Fuß“ nach etwaigen Fehlern
abzusuchen, nur daß man dabei immer noch Gefahr läuft, welche zu übersehen.
Also muß eine andere Methode her, aber auch hier kommt uns Perl zu Hilfe:
Fügen Sie einfach im Kopfbereich des Skriptes folgende Zeile ein:
use strict;
Der Code sieht dann wie folgt aus:
Führt man diesen Code jetzt aus, so passiert das:
Mit dieser Direktive wird Perl dazu gezwungen, nur die Variablen zu akzeptieren, die man vorher explizit deklariert hat. Da hier nichts dergleichen passiert ist, wird sich Perl sogleich beschweren, da es hier einen Fehler erkennt. Also vollführen wir einfach mal eine explizite Deklaration der Variablen $test durch. Perl sieht hierfür das Schlüsselwort my vor. Der so modifizierte Code sieht danach wie folgt aus:
Eine Ausführung dieses Codes ergibt dies:
So ist man die Fehlermeldung für die Deklaration losgeworden, aber bei der Zeile mit der Ausgabe meckert Perl immer noch. Somit ist auch diese Fehlerquelle gefunden und kann abgestellt werden. Wenn man diesen Fehler jetzt korrigiert und das Skript wieder aufruft, so gibt es dies aus:
Damit ist das Skript funktionsfähig, und Sie können es jetzt problemlos
einsetzen. Was sich in diesem Beispiel als trivial darstellt, kann sich in
komplexen Skript als wahre Schnitzeljagd entpuppen, insbesondere wenn sich der
Fehler an einer unübersichtlichen Stelle im Code befindet oder man vor lauter
Code nur noch das sieht, was man sehen will und einem so etwaige Tippfehler gar
nicht mehr auffallen.
Hat man Perl hingegen angewiesen, auf solche Probleme zu achten, so wird es
einen darauf hinweisen und im Bedarfsfalle die Kompilierung des Skriptes
komplett abbrechen. Zudem erhält man so eine ganze Reihe überaus hilfreiche
Meldungen, so daß scheinbar schwer zu findende Fehler plötzlich sehr
offensichtlich werden und so sehr schnell behoben werden können.
Dies nimmt selbst scheinbar schwer zu findenden Fehlern den Schrecken.
Der Tainted Mode
Wo die beiden vorgenannten Möglichkeiten dazu dienten, etwaigen Fehlern im
Skript auf die Schliche zu kommen und so eventuelle Fehlfunktionen zu beheben,
deckt dieser Modus einen anderen Teil ab, der erst zur Laufzeit des Skriptes
relevant wird, aber ebenso zu Fehlfunktionen führen kann: Die Rede ist hier von
Benutzereingaben.
Da es leider immer wieder irgendwelche Idioten gibt, die ein Skript, das
Benutzereingaben erwartet, für unfreundliche Zwecke mißbrauchen, gilt es, diese
Benutzereingaben möglichst so zu bereinigen, daß man damit keinen Unsinn mehr
anstellen kann.
Hier kommt Perls Tainted Mode
ins Spiel, der alle Benutzereingaben als potentiell problematisch kennzeichnet.
Dies ist solange kein Problem, wie diese Daten ausschließlich im Skript selbst
verwendet werden, da sie als Ausgangspunkt für Berechnungen, o. ä. dienen und
somit kein Unheil anrichten anrichten können. Erst wenn diese Daten dazu
herangezogen werden sollen, um potentiell problemträchtige Operationen
auszuführen (z. B. um damit eine Datei zu öffnen, damit Daten hineingeschrieben
werden können), wird Perl mit einem Fehler abbrechen. Somit wird eine
mißbräuchliche Verwendung eines Perlskriptes sehr elegant unterbunden.
Um die Daten jetzt nutzbar zu machen, gilt es zunächst einmal, sie zu
„säubern“, sprich alle problematischen Teile zu entfernen und die so komplett
geprüften Daten zu verwenden. Dadurch haben Sie selbst bei notwendigen
Interaktionen mit Benutzern die Möglichkeit sicherzustellen, daß nichts
anbrennen kann.