Die Woche: Die Sünden der Programmierer

Die Ursache von Sicherheitslücken und Datenverlusten sind oft triviale Programmierfehler. Selbst Open-Source-Entwicklungen sind davor nicht sicher.

In Pocket speichern vorlesen Druckansicht 166 Kommentare lesen
Lesezeit: 4 Min.

Als ein wichtiges Argument für Open-Source-Software wird immer wieder angeführt, dass durch den frei verfügbaren Quellcode Fehler viel leichter zu finden sind als in Closed-Source-Programmen. Schließlich kann jeder selbst im Quellcode nachsehen, wie ein Programm im Detail arbeitet und etwaige Fehler mit einem Patch korrigieren. Trotzdem gelangt immer wieder Code mit eigentlich trivialen Fehlern in Open-Source-Software.

Welch schwerwiegende Folgen solche triviale Programmierfehler haben können, lässt sich gut am Beispiel eines aktuellen Upstart-Bugs nachvollziehen. Anfang April meldete ein Ubuntu-Anwender einen Fehler im Upstart-Skript mounted-tmp.conf, das dafür zuständig ist, unmittelbar nach Mounten der Festplatten das temporäre Verzeichnis /tmp aufzuräumen. Als er das Upstart-Skript testweise via initctl startete, löschte es ohne jegliche Warnung sämtliche Dateien und Verzeichnisse.

Die Ursache für den totalen Datenverlust ist eine typische "kleine" Nachlässigkeit im Upstart-Skript, wie man sie in abertausenden anderen Skripten und Programmen findet: Überspitzt gesagt setzen viele Programmierer voraus, dass alle Anwender die Funktionsweise im Detail kennen und niemals Fehler beim Aufruf oder der Eingabe machen – und verzichten auf dringend notwendige Überprüfungen von Eingaben und Rückgabewerte externer Programme oder Funktionen. Nicht nur Datenverluste sind immer wieder auf solche fehlende Überprüfungen zurückzuführen, sondern auch ein Großteil der in den letzten Jahren entdeckten Sicherheitslücken. Selbst für die Sprengung der ersten Ariane-5-Rakete kurz nach ihrem Start im Jahre 1996 war letztlich eine fehlende Überprüfung von Rückgabewerten im Navigationssystem verantwortlich.

Dabei sind solche Überprüfungen in den meisten Fällen trivial und würden den Code nur um wenige Zeichen vergrößern. Selten, wie beim Ariane-Navigationscomputer, wird wegen mangelnden Ressourcen bewusst auf die Prüfung von Rückgabewerten verzichtet. Oft ist es einfach Nachlässigkeit oder Bequemlichkeit der Programmierer – man möchte mit dem Kern des Programms vorankommen, da stören Werteprüfungen nur den Blick für's Wesentliche. Funktioniert das Programm irgend wann einmal, vergisst man schnell, an welchen Stellen falsche Werte Schaden anrichten oder einen Programmabsturz verursachen können, und veröffentlich den Code ohne die vermeintlich überflüssigen Prüfungen.

Die typischen Sünden unterscheiden sich von Programmiersprache zu Programmiersprache. So schludern die Entwickler in C meistens mit Null-Pointern und sonstiger Zeiger-Arithmetik. Betroffen sind davon nicht nur irgend welche kleinen, für den Hausgebrauch geschriebene Programme, sondern selbst der Linux-Kernel -- 2009 waren Null-Pointer-Fehler die häufigste Fehlerquelle für Sicherheitslücken im Kernel von Red Hats Enterprise-Distributionen.

In Shell-Skripten hingegen vergessen viele Programmierer, dass in Dateinamen und Benutzereingaben durchaus auch Umlaute, Leerzeichen und Steuerzeichen wie Return oder Newline vorkommen können. Weitere beliebte Fehler sind ungeprüfte Variableninhalte und ein fester Glaube daran, externe Programme würden nie mit einem Fehler abbrechen, weshalb man deren Exit-Status getrost ignorieren kann.

Im Fall von Upstart liegt das Problem darin, dass das Skript mounted-tmp.conf aus Ubuntu 10.04 den Pfad zum temporären Verzeichnis aus der Umgebungsvariablen MOUNTPOINT liest – die Programmierer haben jedoch nicht eine einzige Überprüfung eingebaut, ob diese Variable überhaupt gesetzt ist und falls ja, ob der Pfad auch existiert und zugreifgbar ist. Anschließend versucht das Skript, via "cd ${MOUNTPOINT}" in das temporäre Verzeichnis zu wechseln. Fehlt die Variable oder existiert das Verzeichnis nicht, landet mounted-temp.conf automatisch im Root-Verzeichnis. Auch hier fehlt wieder eine Überprüfung, ob der Verzeichniswechsel erfolgreich war. Das ist an dieser Stelle fatal, weil alle folgenden Kommandos von der Grundvoraussetzung ausgehen, dass sie im temporären Verzeichnis ausgeführt werden -- weshalb letztlich nicht übrig gebliebene temporäre Dateien gelöscht werden, sondern sämtliche Dateien und Verzeichnisse auf allen eingebundenen Datenträgern.

Scheinbar vergessen manche Entwickler auch, dass ein Anwender gar nicht weiß, wie ein Programm intern arbeitet. Open Source bedeutet schließlich nur, dass man sich die Quellen ansehen kann, aber nicht zwangsläufig muss. Daher müssen Programmierer jederzeit mit Bedienfehlern rechnen. Dem Anwender bei einem massiven Datenverlust wie im Upstart-Fall vorzuhalten, er hätte das Programm erst gar nicht aufrufen dürfen, während fundamentale Sicherheitsabfragen und Werteprüfungen fehlen, ist nur die faule Ausrede eines noch fauleren Programmieres. Hier wären etwas mehr Gewissenhaftigkeit und Selbstkritik geboten. (mid) (mid)