Document ID: /fineweb-2-swissfilter-quality_10-filterrobots/filtered/03459.jsonl.gz/1607

Wer hat Angst vorm bösen Wolf?
... niemand. Angst haben muss man nur noch vor durchgeknallten Weltimperatoren und Leuten, die Shell-Metazeichen im Namen tragen. Letzteren läßt sich durch einige Überlegungen entgegenwirken, womit sich dieser perlorientierte Artikel beschäftigt.
Gewöhnlich sind sich Webanwendungsentwickler der Notwendigkeit zur Überprüfung von Benutzereingaben bewußt: aufgrund Unachtsamkeit eröffnet sich mit dem fortschrittlichen Stringhandling beliebter Skriptsprachen schnell das ein oder andere Sicherheitsproblem. Werden Parameter eines HTTP-Requests durch ein Programm verarbeitet und bleiben von Wertüberpruefungen verschont, kann besonders im Zusammenhang mit Stringexpansion und Shellaufrufen die Sicherheit des Systems stark gefährdet werden.
Wenn Schüler im Rahmen ihrer Computer AG aktuelle Meßwerte vom Schulbiotop im Netz veröffentlichen und sich ein Upsi-Konstrukt im Code befindet, dann spielt das auch keine Rolle und wenn man von einem fehlerhaften CGI-Programm auf smartfilter.de hört, erheitert es sogar für einen kurzen Moment das triste Dasein. Es sind zumindest Schäden vorstellbar, die dann nicht mehr erheitern.
Die Problemzonen liegen z.b. dort, wo Perlcode und Betriebssystem interagieren. Funktionsaufrufe wie system() und exec() werden gerne mit einem String als Parameter gefüttert:
system("mail -s 'anmeldung' $email_addr");
system() bewirkt in dieser Formulierung das Starten einer Shell, die wiederum das Mailprogramm ausführt. In dieser fahrlässigen system()-Verwendung lassen sich via $email_addr weitere Befehle mit dem Semikolon integrieren, so wie man es von Shellprogrammierung her kennt. Übergibt man Parameter als Liste, wird keine Shell gestartet, wodurch Metazeichen auch nicht expandieren. In einem Fall wie
system('cp', '-R', 'foo-bar', $path);
wäre die Variablenverwendung immer noch kritisch, selbst wenn auf Metazeichen nicht besonders geachtet werden muß. Beim normalen open()-Befehl sowie bei der Verwendung von Backtickkonstrukten ist die Stringform sogar notwendig. Es gibt keine Listenalternative.
Soetwas sollte immer vermieden werden:
open(TMPL, $template_file);
open(TMPL, "< $template_file");
$foo = `cat $file`;
Das erste Beispiel davon ist sogar flexibler misbrauchbar. Beinhaltet der String nur einen Dateinamen, so wird die Datei im Lesemodus geöffnet. Explizite Modusangaben wie ">>" oder ähnliches lassen den Stringparser einen Dateinamen erwarten, da funktionieren dann auch keine Shellmetazeichen. Dagegen bewirken Pipesymbole das Forken einer Shell mit vollem Featureset.
Beim Umgang mit Shells sollten auf jeden Fall die Metazeichen &;`'\"|*?~<>^()[]{}$\n\r gefiltert werden. Für eine weiterführende Diskussion sei auf die Quellen verwiesen .
Bei Skriptsprachenverwendern schon fast in Vergessenheit geraten ist das magische \0-Byte, welches in der Sprache C das Ende einer Zeichenkette symbolisiert. Strings in Perl speichern diesen Terminator problemlos - subtile Probleme können sich ergeben, wenn Perlstrings in C-Code weitergereicht werden und sich ein derartiges \0-Byte einschleicht. \0-Bytes in Perlstrings werden nicht expanded. d.h. auch sie würden mit einfacher Logfilegenerierung via print() nicht erscheinen und sind so schwieriger zu debuggen. Es gibt folgendes open()-Szenario, wo das \0-Byte eine explizite Verwendung findet. Es soll eine Datei geöffnet werden, deren Dateiname führende und anhängende Leerzeichen besitzt. die open-Funktion ignoriert jedoch Leerzeichen an den beiden Positionen. perlopentut empfiehlt als Zaunpfahl für den Tokenizer den Dateinamen mit den Strings "./" und "\0" zu umklammern:
$file =~ s#^(\s)#./$1#; open(FH, "< $file\0") || die "can't open $file: $!";
Ästhetisch ist das sicher nicht. In der Phrack #55 beschreibt rain forrest puppy wie Perls \0-byte-Verhalten in einigen Codekonstrukten misbraucht werden kann. Ein Perlprogramm könnte Benutzerdaten für eine Dateinamensbildung verwenden:
$page = "../../etc/passwd\0"; # vom user definierte zeichenkette
open(FH, "< /data/pages/${page}.html") or die ...
Das Beispiel würde dann nicht mehr die über ein Muster beschriebene HTML-Datei öffnen. Ich wage die Behauptung, daß uns das \0-Byte noch im Jahr 42 auf die Füße fallen wird, wenn Bits durch A-T und C-G representiert werden und wir Faustkeilsprachen wie C für schon längst überwunden geglaubt haben.
In nahezu allen Anwendungsfällen ist die beste Vorkehrung restriktives Filtern. Das meint genau nur Zeichen zu lesen, die erwartet werden. Für eine einfache Umsetzung eignen sich Perls eingebaute reguläre Ausdrücke, die den kompletten String betrachten oder einen unkritischen Substring extrahieren:
if($id =~ m!^\#[\w\d]+$!) { # id ok ... } else { # bad id ... }
Um ein derartiges Vorgehen zu unterstützen, kennt Perl einen taint mode (taint: fleck, makel), in dem extern belegte Variablen nicht mehr ungefiltert als Parameter tendenziel kritischer Befehl verwendet werden können. Ausnamen stellen system() und exec() bei Argumentenlisten und generell print() und syswrite() dar. Darüber hinaus werden weitere einfache Sicherheitschecks durchgeführt. Dieser Modus ist als Hilfestellung zum Auffinden von Problemen zu verstehen. (man perlsec) Sicherheitsüberlegungen werden dem Programmierer dennoch nicht abgenommen, weshalb dieses Feature in der Praxis kaum verwendet wird.
Beim Verwenden von Benutzereingaben aus Formularen im Zusammenhang mit Shellaufrufen ist der Entwickler eigentlich schon im Vorfeld besorgt. Die Gefahren stecken bekanntlich im Detail, weshalb Situation exisitieren, in denen Risiken nicht auf Anhieb erkennbar sind. Werden eingaben einer Webanwendung in externe Programme weitergereicht, wer weiß dann schon konkret, was da passiert? Als Beispiel soll hier das Satzsystem Latex herhalten: Mit etwas Perl und Latex kann man dynamisch Dokumente erzeugen. Werden keine Makroverwendungen in Benutzereingaben gefiltert könnte man mit dem include-Makro den Inhalt lesbarer Dateien einfügen. Somit ließen sich Informationen aus einem Rechner ziehen. Schreiben von Dateien oder gar das Aufrufen von Programmen ist nicht möglich. Doch wer garantiert, daß es sich immer so verhält? Zahlreiche Opensourceprogramme basieren auf Tools, die irgendwann von irgendwem entwickelt wurden. Und bei neueren Versionen? Zwar wird man i.d.R. eine Rückwärtskompatiblität z.b. bei Kommandozeilenparametern anstreben, um Updatestreß zu vermeiden, nur wer stellt sicher, daß neue Features nicht zum Einbrechen benuzt werden können? Da kann man nur hoffen, daß die Entwickler in Kontakt stehen oder andersweitig von neuen Entwicklungen Kenntnis nehmen.
Das Konzept für einen umfassenden und wirkungsvollen Schutz kommt wie immer in die Schublade "Illusionen". Sorgfältige Werteüberprüfung von Parametern ist der empfohlene Weg, der von weiteren Maßnahmen begleitet werden kann. Die konkreten Lösung sollte sich stets am Problem orientieren, denn auch für Fehler aufgrund von Unvorsicht oder Unwissenheit gilt: there is always more than one way to do it.