Sicherheit

Um unnötige Sicherheitslücken zu vermeiden, sollte die statische Analyse ein fester Bestandteil des Software Development Lifecycles sein. Grammatech

Bis zum Jahr 2020 werden laut dem Marktforschungsunternehmen Gartner über 20 Milliarden IoT-Geräte weltweit aktiv sein. Das sind 2,5 IoT-Devices pro Person, wenn man konservative Prognosen zur Bevölkerungsentwicklung der Erde zu Grunde legt. Viele dieser Geräte werden kritische Funktionen in Geschäftsprozessen, in der öffentlichen Verwaltung oder bei der Sicherheit und Gesundheit von Menschen ausüben. Immer komplexere Software und leistungsfähigere Hardware erlauben einen immer größeren Funktionsumfang, das IoT wird sich aller Wahrscheinlichkeit nach zu einem wesentlichen Bestandteil unseres Lebens im Geschäftlichen wie auch im Privaten entwickeln.

Fehler bei diesen Geräten sind dann aber in vielen Einsatzbereichen schlicht nicht akzeptabel. Ein Buffer Overflow im Herzschrittmacher, unbefugter Fernzugriff auf ein fahrendes Auto oder einfach die Manipulation der Sensordaten in der Industrie können Schäden anrichten, die kaum wieder gutzumachen sind. Dass es sich dabei nicht um Bedrohungsszenarien der Science-Fiction-Literatur handelt, haben die vergangenen Jahre gezeigt: Zwei Sicherheitsforschern gelang es zum Beispiel 2015, einen Jeep über das Uconnect-System aus der Ferne zu übernehmen und dabei unter anderem den Motor auszuschalten. Dazu benötigten sie nur ein Notebook und ein Mobiltelefon. Der gleiche Angriffsvektor wurde zudem auch bei einem Modell von Toyota und einem Ford erfolgreich demonstriert. Auch Rückrufaktionen bei einer Insulinpumpe und bei einem Herzschrittmacher gab es bereits, in diesen Fällen konnten die Geräte manipuliert werden.

Schwächen der Sicherheit liegen in der Sprache

Das liegt nicht zuletzt an strukturellen Schwächen der hauptsächlich eingesetzten Sprache C. Bei der Entwicklung von Embedded-Software ist C immer noch eine der populärsten Programmiersprachen: Auch wenn andere Sprachen wie C++, C# oder Java in einigen Fällen zum Einsatz kommen, beruht zum Beispiel über die Hälfte des Codes von Embedded-Automotive-Systemen auf C. Leider ist die Definition, woraus ein zulässiges C-Programm besteht, der Flexibilität zuliebe sehr liberal ausgelegt. Die Compiler können viele Fehler nicht aufdecken. Zudem existieren zahlreiche Mehrdeutigkeiten, die die Compiler basierend auf unterschiedlichen Interpretationen des Standards auflösen müssen. So sind C-Programme sehr anfällig für Speicherzugriffsfehler wie Buffer Overruns und Null-Pointer-Dereferenzierungen. Auch andere Fehlerklassen wie Speicherlecks oder die Nutzung von nicht initialisiertem Speicher treten häufig auf. Die meisten dieser Fehler lassen sich für Angriffe ausnutzen. In der Regel muss ein Angreifer dazu Eingabewerte des angegriffenen Programms, die zum Beispiel von Sensoren, anderen Anwendungen oder auch von Benutzern kommen, für seine Zwecke manipulieren.

In der klassischen IT reagieren die Hersteller mit regelmäßigen Updates auf solche Probleme. Zudem hat sich eine ganze Teilbranche darauf spezialisiert, die Software an Server und Client mit zusätzlichen Produkten gegen Angriffe abzusichern. Doch die oft als „Bananen-Software“ bezeichnete Software, die quasi beim Kunden reifen muss, ist keine Option im Embedded-Bereich. Embedded-Geräte mit den in der IT üblichen Methoden zu schützen, ist schlicht nicht möglich. Viele IoT-Geräte sind mit sehr geringen Übertragungskapazitäten mit dem Internet verbunden. Um die Messdaten eines Sensors zu übertragen, sind wenige Kilobit pro Sekunde völlig ausreichend. Für umfassende Software-Updates sind die Kapazitäten aber zu gering, zumal in einigen der genutzten Funksegmente regulatorisch festgelegte Zeitfenster berücksichtig werden müssen. Darüber hinaus sind die meisten IoT-Geräte weit verteilt oder auch mobil. Auch Firewalling zum Schutz vor unerwünschten Aktivitäten ist nur in sehr begrenztem Maße sinnvoll einsetzbar. Das Ziel muss es also sein, Software mit möglichst wenig kritischen Fehlern auszuliefern.

Sicherheit

Alleine die Datenflüsse in einem Programm können eine hohe Komplexität erreichen, die ohne geeignete Tools nicht mehr zu überschauen sind. Grammatech

Statische Analyse untersucht den Code

Dazu gilt es, Sicherheit zum Design-Prinzip zu erklären und die Qualitätssicherung innerhalb des Software Development Lifecycles (SDLC) als fortlaufenden Prozess zu etablieren, der bereits in einer frühen Phase der Entwicklung greift. Viele etablierte Ansätze wie Testing stoßen dabei aber an ihre Grenzen. Denn Testing bedingt zunächst, dass ein lauffähiger Code vorliegt. Zudem können im Testing nur Fehler gefunden werden, wenn der Code entsprechend durchlaufen wird. Es hängt also von den Testszenarien ab, was sichtbar wird und was nicht.

Einen anderen Ansatz verfolgt die statische Analyse. Dabei wird der Code nicht ausgeführt, sondern in ein Modell überführt. An diesem Modell vollzieht das Analyse-Tool alle Steuerungs- und Datenströme nach. So genannte Checkers überwachen, ob alle Anforderungen an sicheren Code erfüllt sind. Neben Error Conditions wie Buffer Overruns können dabei auch Datenströme erkannt werden, bei denen auf potenziell unsichere Datenquellen zugegriffen wird. Zudem ist es möglich, die Einhaltung von Programmierstandards, internen Best Practices oder Richtlinien zu forcieren.

Vertrauen und Kontrolle

Da die statische Analyse nicht auf lauffähigen, kompilierten Code angewiesen ist, kann dieses Verfahren die dynamischen Testverfahren optimal ergänzen. In der Regel wird bei der statischen Analyse mit dem Quellcode gearbeitet, somit kann sie bereits in einer sehr frühen Entwicklungsphase mit einem hohen Automatisierungsgrad eingesetzt werden. Auch Quellcode von Zulieferern lässt sich damit durchleuchten, die Software Supply Chain wird also um eine effiziente Qualitätskontrolle erweitert. Immerhin stammt laut VDC Research fast 45 Prozent der Code-Basis aktueller Embedded-Projekte von Dritten. Meist handelt es sich dabei um spezielle Komponenten wie Grafik- und Windowing-Toolkits, Kryptografie-Bibliotheken oder Datenbanken. Allerdings: Ein erheblicher Teil dieser Komponenten liegt binär vor, kann also nicht wie Quellcode von den Entwicklern auf potenzielle Sicherheitsprobleme überprüft werden. Eines der wenigen Tools zur statischen Analyse, dass sowohl Binärcode als auch Quellcode untersuchen kann, ist CodeSonar von GrammaTech.

Die statische Analyse ergänzt also das dynamische Testing und hilft so, Fehler im Code zu vermeiden. Entscheidend für sichere Software ist jedoch, dass die Qualitätssicherung als fester Bestandteil des SDLC entlang des gesamten Lebenszyklus etabliert wird. Jede Änderung am Code und jede Bibliothek sollten fortlaufend einem entsprechenden Review unterzogen werden. Hier hilft die statische Analyse, indem sie sich automatisiert in den Entwicklungsprozess integrieren lässt. Das nützt nicht nur der Codequalität, sondern auch dem Budget: Bis zu 80 Prozent des Entwicklungsbudgets geht in das Auffinden und Beheben von Fehlern. Und je früher ein Fehler im SDLC gefunden wird, desto weniger Kosten entstehen bei der Beseitigung. Treten kritische Fehler erst im Testing auf, kann der Schaden groß sein – und noch schlimmer ist, wenn sich der Fehler erst im operativen Betrieb zeigt.

binärcode analysieren

Um den Code auf mögliche Schwachstellen zu untersuchen, muss das Analyse-Tool auf einige Informationen zugreifen, die im Quelltext vorliegen. Bei binärem Code sieht die Sache allerdings anders aus. Der Binärcode von ausführbaren Dateien und Bibliotheken kann auf zwei Arten vorliegen: Er kann Symboltabellen und Debugging-Informationen enthalten (unstripped) oder nicht (stripped). Bei gestrippten Dateien ist eine manuelle Fehlersuche kaum mehr möglich. Nach dem Disassemblieren enthält der Code zu wenig Informationen, er ist nur mit viel Aufwand verständlich und nachvollziehbar. Für die Analyse muss zunächst versucht werden, den unstrukturierten Binärcode wieder zu strukturieren. Diese Informationen müssen aufwändig über sehr komplexe Algorithmen aus dem kompilierten Code generiert werden. Es sind also andere Analysemethoden gefordert als bei der Untersuchung von Quellcode. Auf der anderen Seite erzeugt die Analyse der Binaries auch Informationen, die sich aus den Quellen nicht ableiten lassen. Denn bei der Analyse des Binärcodes wird das Programm so betrachtet, wie es nachher wirklich in den Speicher geladen und ausgeführt wird. Dadurch lassen sich auch mögliche Probleme aufdecken, die durch die Tool-Kette mit Compiler und Linker entstehen können.