MSDN Magazin > Home > Ausgaben > 2006 > November >  Secure Habits: 8 einfache Regeln zum Entwi...
Secure Habits
8 einfache Regeln zum Entwickeln von sichererem Code
Michael Howard

Themen in diesem Artikel:
  • Using analysis tools and experts to review your code
  • Reducing risk using fuzzing and threat modeling
  • Keeping bad input out of your applications
  • Learning all you can about security concepts
In diesem Artikel werden folgende Technologien verwendet:
Ich hatte das Glück, im Lauf der Jahre mit Tausenden guter Entwickler zusammen zu arbeiten, die wirklich daran interessiert waren zu lernen, wie man sicherere Software schreibt. Während dieser Zeit habe ich auch eine Menge von Leuten gelernt, die im Erstellen sicherer Systeme sehr gut waren, und das gab mir zu denken. Gibt es vielleicht Fähigkeiten oder Arbeitsweisen, die allen "sicheren Entwicklern" gemeinsam sind? Und tatsächlich, die Antwort ist ein klares „Ja“. In diesem Artikel wird die Liste der Arbeitsweisen vorgestellt, durch die sich Entwickler von sicherem Code auszeichnen.
Ganz ohne jeden Zweifel wird jeder, der sich diese Liste ansieht, darin sofort nicht genannte Arbeitsweisen vermissen. Das ist ganz normal. Natürlich gibt es noch viele andere großartige Ideen. Dies ist meine ganz persönliche Liste! Nachdem das geklärt wäre, sollen nun die im Lauf der Jahre festgestellten beispielhaften Arbeitsweisen beschrieben werden.

Arbeitsweise Nr. 1: Übernehmen Sie Verantwortung
Das ist eine Variante des klassischen Kommentars "Es gibt keine Silberkugel", den Fred Brooks vor mehr als 25 Jahren in Vom Mythos des Mann-Monats gab. Ob im Hinblick auf die Sicherheit alles in Ordnung ist, hängt ganz allein von Ihnen ab. Niemand sonst – und ganz bestimmt nicht irgendein magisches Tool oder eine Programmiersprache – löst alle Sicherheitsprobleme. Damit keine Missverständnisse aufkommen: Quellcode-Analysetools haben durchaus ihre Berechtigung, aber sie sind kein Allheilmittel gegen alle Ihre Sicherheitslücken. Nur Sie können wirklich Abhilfe schaffen.
Sichere Produkte werden von Entwicklern gemacht, die sichere Entwürfe erstellen und sicheren Code schreiben. Denn letzten Endes ist das Schreiben von Code ein ganz individuelles Unterfangen. Sie sind ein Individuum und können nicht durch ein Tool ersetzt werden. Deshalb liegt die Sicherheit Ihres Produkts in Ihrer Verantwortung! Die Würmer Blaster und CodeRed nutzten Code aus, der von einzelnen Personen geschrieben worden war (siehe Abbildung 1).
Abbildung 1 Entwickler schreiben Code mit Schwachstellen 
Bedenken Sie, dass jeder Code genau untersucht und möglicherweise angegriffen wird. Das ist vollkommen in Ordnung. Angegriffen zu werden ist ganz normal. Die große Frage ist nur, ob Ihr Code davon in Mitleidenschaft gezogen wird. Nur Sie können hier die richtigen Weichen stellen. Also seien Sie stolz auf Ihren Code. Sie müssen mit der Qualität Ihres Codes zufrieden sein und nachts ruhig mit dem Gedanken einschlafen können, dass Sie alles in Ihrer Macht Stehende getan haben, um zu verhindern, dass der Code bei einem Angriff in die Knie geht.
Wenn dies irgendwie möglich ist, lassen Sie Ihren Code von einem Kollegen prüfen, der Sicherheitsexperte ist. Lassen Sie ihn nicht von jemandem prüfen, der von Sicherheit keine Ahnung hat. Sie können nicht erwarten, dass dieser Person Sicherheitsfehler und Schwachstellen auffallen. Bemühen Sie sich insbesondere, jemanden zu finden, der wirklich versteht, worauf es ankommt, wenn er sich den Code ansieht.
Und seien Sie sich nicht zu fein, um Hilfe zu bitten, wenn Sie sie benötigen. Wie weiter oben gesagt, sollten Sie sich nicht allein auf Tools verlassen, aber Sie sollten auf jeden Fall die Vorteile aller Tools nutzen, die Ihnen ohne weiteres zur Verfügung stehen. Führen Sie mit Ihrem Code alle verfügbaren Quellcodeanalyse-Tools aus, und zwar immer wieder. Nutzen Sie jedes der Verteidigung dienende Konstrukt Ihrer Programmiersprache und jeden Ihnen verfügbaren Trick mit Bibliotheken aus. In C# sollten Sie zum Beispiel im Netzwerk ausgeführten Code, der einen Arrayzugriff ausführt, bei dem der Arrayindex von einer Netzwerkanfrage abgeleitet wird, in geprüfte Operatoren wrappen, um mögliche Fehler in der Ganzzahlarithmetik zu erkennen.

Arbeitsweise Nr. 2: Vertrauen Sie Daten nie
Es wurde schon unzählige Male gesagt und muss auch hier wiederholt werden: Jede Eingabe ist böse, solange nicht das Gegenteil bewiesen ist. Wenn Sie sich die gefährlichsten Sicherheitsschwachstellen ansehen, dann ist ihnen allen gemeinsam, dass der Entwickler den ankommenden Daten vertraut hat. Das Problem ist Folgendes: Wenn Ihr Code annimmt, dass die Daten richtig gebildet sind, was passiert dann, wenn diese Annahme falsch ist? Wenn Sie einen guten Tag erwischen, stürzt Ihre Anwendung wahrscheinlich einfach ab. Wenn Sie einen schlechten Tag erwischen, könnte der Angreifer bösartigen Code in Ihren Prozess einfügen und Verwüstungen anrichten.
Die in gewisser Weise kuriose Definition eines sicheren Systems lautet, dass es eines ist, das die Aufgaben ausführt, die es soll, und keine sonst. Aber wenn es Probleme im Zusammenhang mit der Vertrauenswürdigkeit von Eingaben gibt, dann können Sie das System normalerweise dazu bringen, dass es auch andere Aufgaben ausführt. Ein flüchtiger Blick auf die Daten über häufige Schwachstellen und Risiken (Common Vulnerabilities and Exposures, CVE) unter cve.mitre.org (möglicherweise in englischer Sprache) zeigt, dass von 2001 bis 2004 47 Prozent aller von CVE verfolgten Sicherheitslücken die Vertrauenswürdigkeit von Eingaben betrafen. Die bekanntesten Probleme sind Pufferüberläufe, Fehler in der Ganzzahlarithmetik, siteübergreifende Skripts und Fehler im Zusammenhang mit SQL Injection. Nach und nach kommen Variationen zu diesem Thema auf, beispielsweise Risiken durch XPath Injection und LDAP (Lightweight Directory Access Protocol) Injection.
Probleme im Zusammenhang mit der Vertrauenswürdigkeit von Eingaben können Sie durch die Beachtung einiger weniger Regeln in den Griff bekommen. Suchen Sie zunächst einmal nicht nur nach den Dingen, von denen Sie wissen, dass sie Schaden anrichten; das würde ja bedeuten, dass Sie genau wissen, was alles Schaden anrichtet, und auch alles, was in Zukunft Schaden anrichten könnte. Nach dem zu suchen, was Schaden anrichtet, ist in Ordnung, solange das nicht Ihre einzige Verteidigungsmaßnahme ist. Eine bessere Strategie besteht darin, Eingaben auf das zu beschränken, von dem Sie wissen, dass es unschädlich ist. Für komplexe Programmiersprachen wie C# und Perl bietet sich hierfür die Verwendung regulärer Ausdrücke an.
Weisen Sie als Nächstes das ab, von dem Sie wissen, dass es Schaden anrichtet. Wenn zum Beispiel jemand aus der Ferne eine Datei über Ihren Code anfordert und der Dateiname ein verdächtiges Zeichen enthält (wie : oder \), weisen Sie die Anforderung ab. Sagen Sie dem Angreifer dabei nicht, warum; sagen Sie einfach nur "Datei nicht gefunden".
Und schließlich – aber das funktioniert nicht in allen Szenarios – „desinfizieren“ Sie die Daten. Zum Beispiel sollten Sie im Fall eines Webservers Ausgaben, die aus einer potenziell nicht vertrauenswürdigen Eingabe stammen, HTML-codieren.

Arbeitsweise Nr. 3: Modellieren Sie Bedrohungen bezogen auf Ihren Code
Sie haben doch sicherlich Bedrohungsmodelle. Bedrohungsmodelle ermöglichen es Ihnen, die potenziellen Risiken für Ihre Software zu verstehen und dafür zu sorgen, dass Sie die geeigneten Abwehrmaßnahmen treffen. Doch die Vorzüge der Bedrohungsmodellierung gehen über einen sicheren Entwurf hinaus. Mit Bedrohungsmodellen können Sie auch die Qualität Ihres Codes verbessern. Bedrohungsmodelle sagen Ihnen, woher die Daten kommen. Sind es Remote- oder lokale Daten? Stammen die Daten von anonymen Benutzern oder von eher vertrauenswürdigeren (authentifizierten) Benutzern, vielleicht Administratoren?
Anhand dieser Informationen können Sie feststellen, ob Ihre Verteidigungsmaßnahmen angemessen sind. Zum Beispiel ist es ratsam, dass Code, auf den anonyme und Remotebenutzer zugreifen können, sehr sicherer Code ist. Das bedeutet nicht, dass bei Code, auf den nur lokale Administratoren Zugriff haben, die Sicherheit keine Rolle spielen würde. Es bedeutet vielmehr, dass Code, auf den ein Remotezugriff möglich ist, insbesondere standardmäßig ausgeführter Code, eben bombensicher sein muss. Und das heißt mehr Verteidigung, gründlichere Prüfung und Beachtung auch kleinster Details. Darüber hinaus kann das Bedrohungsmodell Ihnen sagen, von welcher Art die zu schützenden Daten sind. Wertvolle Geschäftsdaten und personenbezogene Daten müssen zum Beispiel besonders gut geschützt werden. Sind Ihre Verteidigungsmaßnahmen angemessen?
Vergewissern Sie sich, dass Ihre Bedrohungsmodelle genau und aktuell sind. Ermitteln Sie danach alle Einstiegspunkte in Ihren Code, und ordnen Sie sie nach Zugriffsmöglichkeiten: remote oder lokal, Benutzer mit hoher oder niedriger Berechtigungsebene (oder gar keiner Berechtigung). Der am leichtesten zugängliche Code sollte am gründlichsten und als erster überprüft werden. Überprüfen Sie schließlich den gesamten Code entlang anonymer Datenpfade. Mit anderen Worten: Beginnen Sie an jedem Einstiegspunkt, an dem ein anonymer Zugriff möglich ist, verfolgen Sie die Daten entlang dieses Pfades, und kontrollieren Sie dabei den Code auf Korrektheit.

Arbeitsweise Nr. 4: Halten Sie Ihren Vorsprung
Die Sicherheitslandschaft ist in ständiger Bewegung. Zum Thema Sicherheit gibt es offenbar jede Woche neue Variationen. Das bedeutet, dass Sie sich selbst weiterentwickeln und etwas über neue Bedrohungen und Verteidigungsmaßnahmen lernen müssen – oder Sie müssen die Konsequenzen tragen.
Zu den einfachen Strategien, mit denen Sie Ihren Vorsprung halten, gehört es, immer wieder einmal ein gutes Buch zum Thema Softwaresicherheit zu lesen. Lernen Sie auch aus Ihren früheren Fehlern oder, noch besser, aus den Fehlern der anderen. Das können Sie, indem Sie bugtraq lesen: Rufen Sie dazu securityfocus.com auf, und lassen Sie sich für die regelmäßige Zustellung der bugtraq-Einträge (möglicherweise in englischer Sprache) an Ihren Posteingang registrieren. Doch beachten Sie einen guten Rat: Erstellen Sie eine Posteingangsregel, die die Einträge in einen speziellen Ordner verschiebt, damit Sie in den großen Mengen Informationen nicht die Übersicht verlieren. Das ist wichtig.

Arbeitsweise Nr. 5: Testen Sie mit zufälligen Daten!
Fuzzing ist eine Testtechnik, die erfunden wurde, um Zuverlässigkeitsfehler zu finden. Es stellt sich heraus, dass ein Teil der Zuverlässigkeitsfehler Sicherheitslücken sind, die quasi nur darauf warten, ausgenutzt zu werden. Gewiss, ein Pufferüberlauf könnte eine Anwendung zum Absturz bringen, aber wenn eine intelligent gemachte, böswillige Nutzlast vorhanden ist, kommt es womöglich gar nicht zum Absturz, und der Angreifer könnte Code ausführen, um stattdessen seine eigenen Ziele zu verfolgen. Das Motto hier bei uns lautet: "Was heute Denial of Service ist, ist morgen Ausführung von Code".
Praktisch jeder Fehler/jede Schwachstelle in der Dateianalyse wurde durch reines Glück oder durch Fuzzing gefunden. Microsoft hat Sicherheitslücken durch Analysieren einer Reihe von Dateiformaten gefunden, darunter XLS-, PPT-, DOC- und BMP-Dateien. Die meisten Anbieter hatten ähnliche Schwachstellen, weil das Analysieren komplexer Datenstrukturen eine komplexe Aufgabe ist, weil ein komplexer Code eben Fehler hat und weil einige dieser Fehler sich als Sicherheitslücken erweisen werden.
Sie müssen sämtlichen Code, der Dateien und den Netzwerkverkehr analysiert, mit zufälligen Daten testen. Der Sicherheitsentwicklungszyklus (Security Development Lifecycle, SDL) bei Microsoft drückt sich im Hinblick darauf, was das für Dateiformate bedeutet, sehr klar aus. Sie müssen alle Parser mit 100.000 Iterationen fehlerhafter Dateien mithilfe eines Dateifuzzers testen. Einige brauchbare Fuzzer stehen zur Verfügung, und ein Dateifuzzer ebenso wie C++-Quellcode wurden in das von mir zusammen mit Steve Lipner verfasste Buch The Security Development Lifecycle (microsoft.com/MSPress/books/8753.asp) aufgenommen.
Noch ein letztes Wort zum Fuzzing: Wenn Ihr System abstürzt, denken Sie nicht, das es nichts weiter als eben ein Absturz sei. Wahrscheinlich ist ein guter Teil dieser so genannten Abstürze eine explizite Aufforderung an jemanden, einen Exploit zu schreiben. Sie sollten einen Absturz also nicht einfach abtun als "nur ein Absturz".

Arbeitsweise Nr. 6: Schreiben Sie keinen unsicheren Code
Bei Microsoft arbeiten wir mit dem Konzept der „Qualitäts-Gates“, um die Möglichkeiten zu verringern, dass ein Entwickler Code mit Schwachstellen in das Produkt eincheckt. Die „Gates“ führen mit dem Code vor dem Einchecken ein ganzes Arsenal von Quellcode-Analysetools aus, um alle Probleme mit Kennzeichen zu versehen. Und alle festgestellten Probleme müssen behoben werden, bevor das Einchecken abgeschlossen werden kann. Sie können auch strenge Coderegeln wie den Ausschluss der Verwendung von gesperrten Funktionen durchsetzen, zum Beispiel keine Aufrufe von „strcpy“ oder „strncat“ und keine unsichere Verschlüsselung. (Microsoft hat über 100 C-Laufzeitfunktionen für neuen Code gesperrt!) Zum Beispiel lassen wir für die Verschlüsselung DES (Länge des Schlüssels ist zu gering), MD4 oder MD5 (sind inzwischen beide geknackt) in neuem Code nicht zu, außer wenn ein Industriestandard ihre Verwendung vorschreibt.
Erfinden Sie vorhandene Funktionen nicht neu. Wenn Sie Code haben, der ein bestimmtes Dateiformat analysiert, brauchen Sie nicht zwei oder drei Analysecodesätze; bleiben Sie bei dem einen Satz, sorgen Sie dafür, dass er stabil ist, und wrappen Sie ihn in eine Form, die projektübergreifend genutzt werden kann.
Und vergessen Sie nicht, dass Tools kein Ersatz für gründliche Kenntnisse über das Schreiben von sicherem Code sind. Deshalb ist die Mitarbeiterschulung in Sicherheit und Datenschutz so wichtig. Sie benötigen ein fundiertes Wissen über die Konzepte, um zu den Urteilen und Einsichten zu gelangen, zu denen Ihre Tools nicht in der Lage sind.

Arbeitsweise Nr. 7: Erkennen Sie die strategische Asymmetrie an.
Das ist ganz besonders wichtig. Bedenken Sie, dass Sie als Softwareentwickler in punkto Sicherheit die schlechteren Karten haben. Mit anderen Worten: Der Angreifer ist im Vorteil, und der Verteidiger steckt im Dilemma, oder, wie man auf Englisch sagt: "Attacker's Advantage and Defender's Dilemma". Sie müssen erreichen, dass der Code und die Entwürfe über 100 % der Zeit 100%ig korrekt sind, und das ist unmöglich. Um alles noch schlimmer zu machen, müssen Sie dieses unerreichbare Ziel mit einem festen Budget termingerecht erfüllen und gleichzeitig Anforderungen im Hinblick auf Unterstützungsfähigkeit, Kompatibilität, Zugriffsfähigkeit und weitere Fähigkeiten unterstützen." " Ein Angreifer hat alle Zeit der Welt, um einen Fehler zu finden und anschließend zu verkünden, dass Ihre Anwendung unsicher ist.
In Arbeitsweise Nr. 6 wurde gesagt, dass Sie keinen neuen unsicheren Code mehr schreiben sollten. In Arbeitsweise Nr. 7 geht es darum, dass Sie den gesamten Code im Blick haben sollten, denn Angreifer greifen jeden Code an, egal wie alt er ist. Nehmen Sie sich die Zeit, alten Code auf Sicherheitslücken zu überprüfen, und ziehen Sie ernsthaft in Betracht, alte, unsichere Funktionen zu verwerfen. Wenn Sie flexible Entwicklungsmethoden nutzen, sollten Sie darüber nachdenken, einen oder auch mehrere schnelle Durchläufe speziell zum Korrigieren von altem Code zu verwenden, um ihn auf das Qualitätsniveau des neueren Codes anzupassen.

Arbeitsweise Nr. 8: Verwenden Sie die bestmöglichen Tools
Ein guter Rat zum Schluss: Verwenden Sie die besten Tools, die Sie können. Quellcodeanalyse-Tools sind eine wunderbare Sache, und ebenso jede Technologie, die Sie beim Schreiben von sichererem Code unterstützt. Wie gesagt, Tools sind kein Allheilmittel, aber sie helfen. Und zwar eine Menge! Tools helfen auch, das Problem der Quellcodeanalyse in den Griff zu bekommen. Tools können riesige Mengen Code rasch und viel schneller scannen, als es einem Menschen je möglich wäre. Und das hilft Ihnen, ein Gefühl dafür zu bekommen, wie "böse" ein Stück Code sein könnte.
Ein beliebter Trick besteht darin, Code mit der jeweils höchstmöglichen Warnstufe zu kompilieren, zum Beispiel /W4 in Verbindung mit Visual C++® oder -Wall in Verbindung mit gcc. Wenn Sie sehr viele Warnungen im Code sehen, hat der Code vielleicht andere Fehler, die vom Compiler oder den anderen Tools nicht gefunden wurden. Ein solcher Code sollte vor seiner Auslieferung einer besonders sorgfältigen Prüfung unterzogen werden (siehe Arbeitsweise Nr. 3).
Dies sind acht bewährte Arbeitsweisen, nach denen Entwickler, die ich sehr schätze, sowohl innerhalb als auch außerhalb von Microsoft vorgehen. Diese Arbeitsweisen allein machen Sie noch nicht zu einem brillanten sicheren Entwickler, aber sie werden Ihnen sicher auf dem Weg dorthin behilflich sein.

Michael Howard ist Senior Security Program Manager bei Microsoft, spezialisiert auf die Verbesserung von sicheren Prozessen und auf bewährte Methoden („Best Practice“). Er ist Mitverfasser von fünf Büchern zum Thema Sicherheit, die auf Englisch unter den folgenden Titeln erschienen sind: The Security Development Lifecycle, Writing Secure Code, and 19 Deadly Sins of Software Security.

Page view tracker