Secure Coding
Warum es gerade jetzt wichtig ist.

Secure Coding / Secure Development

Zunächst möchte ich etwas betrachten, was bereits in der Vergangenheit durch Ken Thompson nachgewiesen wurde. Dabei war die Idee, nicht direkt das Programm, in diesem Fall UNIX, sondern den Compiler des Sourcecodes zu kompromittieren. (1)(2)
Beim Kompilieren des Passwortmoduls, hat der Compiler festgestellt, dass er nun genau dieses kompiliert und ein paar Zeilen „hinzugefügt“. Dadurch wurden in allen Versionen, welche mit diesem Compiler gebaut wurden, das Password „ken_thompson“ ebenfalls akzeptiert -zusammen mit dem eigentlichen Passwort. Dies passierte im Jahr 1984 und war zu diesem Zeitpunkt der Nachweis darüber, dass nicht nur der Code, sondern auch die Werkzeuge mit welchen dieser erzeugt und kompiliert werden, sicher sein müssen.

Repositories

Im Dezember 2020 wurde die Firma Solarwinds durch Hacker, vermutlich aus Russland, angegriffen. Dabei wurden die Buildpipeline des Unternehmens, besonders für die Produkte Orion (eEine Assetmonitoring Plattform) und der Serv-U FTP Server (ein Datei Server) kompromittiert. (3) Ich möchte an dieser Stelle weniger auf die - ebenfalls hochinteressanten - Datenleaks bzw. die Auswirkungen des Ganzen eingehen, sondern vielmehr betrachten, was technisch passiert ist. Dies betrifft im Wesentlichen die Punkte „Repositories“, „Buildpipeline“ und „Zertifizierungskette“. Wobei bei letzterem noch die Fragestellung besteht, ob dies Symptom oder Ursache des Hacks ist.

Die „Hacker“ – ich nutze es beabsichtigt in Anführungszeichen, da es sich per Definition eher um Cracker handelt, aber inzwischen so in den Sprachgebrauch übergegangen ist - haben sich bei Solarwinds zunächst Zugriff auf die Quellcodes von zumindest den oben genannten Produkten Orion und ServU verschafft. Dabei haben sie Einblicke auf die Funktionsweise und die Struktur sowie mögliche Schwachpunkte dieser Produkte erhalten können, welche auch für weitere Hacks in der Zukunft durchaus zum Einsatz kommen könnten.

Dies ist für mich der erste kritische Punkt. Denn Software ist selten (in meiner bisherigen Laufbahn ist dies zumindest noch nicht vorgekommen) fehlerfrei. In der Regel steigt mit der Komplexität eines Softwareprodukts auch die Fehleranzahl. In den letzten Jahren ist durch die Nutzung von Frameworks, welche wiederum andere Frameworks benutzen, die Komplexität von neuer Software stark angestiegen. Hier würde ich gerne eines meiner Lieblingsbeispiele anbringen: Während die Software des Spaceshuttles „Columbia“ ca. 400.000 Zeilen Code hatte (ca. 1960), besteht ein kleiner Webshop aus dem Jahr 2021 -in Anbetracht aller genutzten Bibliotheken- locker aus über 4.000.000 Zeilen Code, davon vieles ungenutzt. Die eingesetzten Bibliotheken haben meist mehr Funktionalität als für den Einsatzzweck überhaupt notwendig. Damit bringen Sie aber durch Schnittstellen oder „Features“, welche nicht genutzt werden und damit ggf. nicht durch die Entwickler betrachtet werden (das „auf dem Schirm haben“) mit. Es muss also bei der Bibliotheken-Nutzung nicht nur der eigene Code, sondern auch der von dritten betrachtet und - im Sinne von Secure Coding - untersucht werden. Ich komme auf diesen Punkt zurück.

Buildpipeline

Der zweite kritische Punkt ist für mich die „Buildpipeline“. Dabei ist nicht nur zum Beispiel ein Jenkins-Jjob zu betrachten sondern alle Tools, Server und Scripte, welche zum Entwickeln und zum „Bauen“ der Software genutzt werden. Im Prinzip muss man dies bis auf Betriebssystemebene betrachten. In der Annahme, dass es auf dieser Ebene eine ausreichende Sicherung gibt (hier würde man sich in Details und vielen Seitenannahmen verlieren), möchte ich mir die Tool-Landschaft anhand eines Beispiels ansehen – auf die Betrachtung des Codes verzichte ich an dieser Stelle bewusst - dabei lege ich meine Umgebung für private Projekte zugrunde:

Buildpipeline
Buildpipeline

Nehmen wir an, zur Entwicklung wird ein Visual Studio Code (VSC) mit Plugins für Java verwendet, Code wird per Git auf einem Server verwaltet, und die Builds werden automatisch durch einen Jenkins-Server ausgeführt. Am Ende der Pipeline wird noch per Skript auf eine virtuelle Maschine (VM) ausgerollt, in meinem Falle ein Docker Container, und gestartet. So oder so ähnlich kann man dieses Setup auch bei diversen Firmen im Einsatz vorfinden. Es finden sich bereits die ersten Kompromittierungsvektoren im VSC, der Code ist zwar OpenSource und für jeden einsehbar, gleichzeitig ist es aber für ein einzelnes Projekt eine kaum zu stemmende Aufgabe, dies komplett zu durchleuchten (außer dies ist Ziel eines Projektes). Das gleiche „Problem“ stellt sich auch mit anderen Entwicklungsumgebungen (IDE) (wie Eclipse oder Netbeans), dabei sind für mich Closed Source Umgebungen noch problematischer, da dort nicht mal selbstständig „nachgesehen“ werden kann, sondern man sich auf die Aussagen des Herstellers verlassen muss. Das Problem mit eventuell kompromittierten IDEs wird durch die Nutzung von Plug-Ins noch verschärft, der Verzicht kann hier ebenfalls nicht der Weg sein, da z. B. VSC ohne Plug-Ins nichts kann. Auch die Plug-Ins sind Oopen Source und bedeuten viel zu prüfenden Code. Der entwickelte Code selbst würde dann per Git verwaltet werden, auch ein Git kann jedoch falsch eingerichetet oder angegriffen werden. Dazu zunächst der kurze Hinweis, dass schon öfter Entwicklerteams sensible Daten auf einem öffentlichen Git Server (z. B. github) abgelegt haben, obwohl diese sogar explizit private Repositories anbieten. Jedoch kann auch ein selbst eingerichteter Git Server zum Ziel werden, dabei wäre es zum einen möglich, dass der Server fehlerhaft eingerichtet oder auch hier das Tool selbst kompromittiert wurde. Eine theoretische Möglichkeit wäre ein „geknacktes“ Git (ich schreibe von der Serverkomponente), welches entweder eine Backdoor öffnet oder den eingecheckten Code nach außen kopiert.
Die nächste Komponente in diesem Konstrukt ist der Jenkins-Sserver (auch hier gibt es natürlich andere Varianten). Sofern jemand auf diesem Server Zugriff hat, können zum Beispiel Codeprüfungen deaktiviert, ein anderes Repository (mit „besseren Bibliotheken“), Passwörter eingesehen und Prozesse verändert werden. Zwar hat der Jenkins selbst eine History darüber, welche Jobs von wem konfiguriert wurden, jedoch nicht darüber, welche Einstellungen von wem durchgeführt wurden. Es ist zum Beispiel durch Anpassung der Jobs möglich, einen anderen Java Truststore zum Bauen mitzugeben, damit wäre im Prinzip ein großer Teil der gut gemeinten Security ausgehebelt.

Der letzte Teil meines Gedankenexperimentes sind die Skripte zum Ausbringen und Starten von gebauten Artefakten auf einer VM. Diese sehe ich noch am wenigsten kritisch, da diese im Normalfall übersichtlich gehalten sind und imperativen Code, also keine Klassen o. ä. enthalten. Jedoch können auch diese Skripte verändert werden und zum Beispiel für einen Rollout weitere Parameter mitgeben, oder weitere -ungewollte- Artefakte ausbringen.

Wie kann man sich nun gegen diese Bedrohungen schützen?

Auch wenn ich in den letzten Absätzen zunächst den Teufel an die Wand und ein Worst-Case Szenario gemalt habe - es ist nicht alles verloren. Die IDEs und Plug-Ins sollten nur aus vertrauenswürdigen Quellen heruntergeladen werden, dabei sollten Hashwerte zum Vergleich des Paketes mit dem berechneten Wert vom Hersteller herangezogen werden.

Hashwerte bei Visual Studio Code
Hashwerte bei Visual Studio Code

Bei kritischen (in Bezug auf deren Sicherheit, Behörden o. Ä.) Projekten sollte zusätzlich eine Codeanalyse der Quellen der IDEs durchgeführt werden. Unangekündigte -dringende- Updates müssen mit dem Herausgeber hinterfragt werden, denn ein als Update getarnter Hack wäre nicht das erste Mal. Die Nutzung von Closed Source (im Sinne von Black Box, nicht im Sinne der Lizenz) Produkten kann ich an dieser Stelle nicht empfehlen, da man sich auf die Aussage des Herausgebers verlassen muss, ohne selbst prüfen zu können. Im Falle von Java sollte ebenfalls eher ein OpenJDK, als das „Original“ von Oracle verwendet werden, aus denselben Gründen. Für die Pipeline gibt es ebenfalls Möglichkeiten, diese bezüglich einer Kompromittierung resilient zu gestalten. Eine davon, wäre, dass über die eingerichteten Jobs bzw. die hinterlegte Konfiguration ebenfalls Hashwerte gebildet werden, welche regelmäßig überprüft werden. Bei einer Abweichung muss entsprechend geprüft werden, warum diese aufgetreten ist. Dieselbe Herangehensweise kann für Automatisierungsskripte genutzt werden: Zunächst eine Prüfung auf deren Integrität, anschließend Ausführung.

Und warum ist das gerade jetzt so wichtig?

Es war schon immer wichtig, aber die wachsende Komplexität, bereits von einfachen Anwendungen, zeigt, dass  diverse Vektoren existieren, über welche Kompromittierungen auf Build-Prozesse geschehen könne. In der heutigen Welt gibt es Abhängigkeiten zwischen Firmen, von den nur Notiz genommen wird, wenn bereits etwas passiert ist. Dies macht das Beispiel von Solarwind deutlich, dort wurde - vermutlich über ein kompromittiertes Werkzeug - das ausgerollte Produkt so verändert, dass dieses Hintertüren bei den Kunden von Solarwinds geöffnet hat. Dabei hat sich das veränderte Produkt als vermeintliches Update ausgegeben. Was wäre, wenn zu einem vermeintlichen Update für ein Betriebssystem (egal welches) geführt hätte, welches im ungünstigsten Fall automatisch weltweit ausgerollt worden wäre? Der Schaden wäre nicht kalkulierbar und könnte eventuell sogar die finanziellen Schäden von beispielsweise Emotet (4) in den Schatten stellen.

Ich möchte mit dem Fazit schließen, dass eine einhundertprozentige Sicherheit kaum erreichbar ist, Software-Build-Prozesse jedoch zumindest so gestaltet werden können, dass Kompromittierungen erkannt werden können und ein Rollout von einer beschädigten Software verhindert werden kann.

Gebt auf eure Buildpipeline acht!

  • Statische und Dynamische Codeanalyse (z. B. Sonarqube)
  • Bereitstellung von Buildtools nur aus Zertifizierten Quellen, dies betrifft
    - Den Compiler (Javac, gcc, etc.)
    - Die Entwicklungsumgebung
    - Die Buildtools (zB. Maven, Ant, Gradle)
    - Die CI/CD Tools (z. B. Jenkins)
  • Die Analysetools
  • Absicherung der Codeverwaltung
    - Zum Beispiel Absicherung von Git durch geeignete Schlüssel statt Username/Passwort
  • Prüfung der eingesetzten Bibliotheken
    - Download nur aus zertifizierten Quellen
    - Einsatz von Bibliotheksverwaltungen mit White-/Blacklisting wie zum Beispiel Nexus IQ
  • Spezialisierte Tests von kritischen Codestellen
    - Dies betrifft insbesondere Login und Rechteverwaltungen
    - In besonders kritischen Fällen ist auch das Kompilat zu prüfen

Autor und Ansprechpartner

Malte Koch, Cassini Consulting

Malte Christjan Koch

Senior Consultant

malte.koch@cassini.de
Seite teilen
Newsletter Cassini

Newsletteranmeldung

Sie möchten regelmäßig die neuesten Informationen zum Thema Digitalisierung und digitale Transformation erhalten und über spannende neue Jobangebote informiert werden?
Melden Sie sich kostenlos für unseren Newsletter an.


Ja, ich möchte den regelmäßigen Newsletter von Cassini erhalten. Weitere Informationen finden Sie in unseren Datenschutzbestimmungen.

*Pflichtangaben