Über unsMediaKontaktImpressum
Christian Kühn 04. Mai 2021

DevSecOps – ein "Gratis"-Einstieg mit Open-Source-Tools

Dieser Beitrag soll eine kurze Einführung in das Thema DevSecOps darstellen. Mittels Beispiel-Tools wird veranschaulicht, wie Entwicklungsteams, die wenig Vorerfahrung im Themenbereich Information Security haben, ihre Software mittels Sicherheits-Analyse gegen bereits bekannte Schwachstellen absichern können. Jegliche Verbesserungen bzw. Tests sollen im DevOps-Gedanken automatisiert erfolgen.

Was ist eigentlich DevSecOps?

DevSecOps knüpft an DevOps an und bringt zusätzlich zum Dev- und Ops-Blick noch die Betrachtung aus Security-Sicht mit in die cross-funktionale Entwicklung ein. Während bei DevOps eine sehr breite Begriffsauslegung existiert, die zwischen Aufgaben-Beschreibung und Arbeitskultur schwankt, findet man zum Thema DevSecOps überwiegend folgendes:

DevSecOps beschreibt die Suche und Absicherung vorhandener Schwachstellen in der Softwareentwicklung, die bereits während der Entwicklungszeit stattfindet.

Im Vergleich zu klassischer Analyse durch ein QA- und/oder Security-Team, das die Software nach der Entwicklung analysiert oder eine Abnahme vornimmt, findet man im DevSecOps-Sales-Sprech oft den Slogan "shifting left", der beschreibt, dass man bereits in der Entwicklungszeit Probleme erkennen und adressieren kann. Shifting left betrachtet hier einen Zeitstrahl, auf dem die Sicherheitstests weiter vorne (also links) ausgeführt werden.

Konkret heißt das, dass vorhandene Continuous-Integration-Strukturen um neue Tests erweitert werden, die auf die Software-Sicherheit abzielen.

Diese Tests erkennen beispielsweise, ob in Drittanbieter-Bibliotheken bekannte Schwachstellen vorhanden sind (s. Bsp. unten). Diese Schwachstellen können beliebiger Form sein, von Angriffsmöglichkeiten wie SQL Injection, mit denen Angreifer Teile einer Datenbank auslesen könnten, aber auch kleinere Probleme, die z. B. die Anwendung "nur" verlangsamen. Weitere Möglichkeiten wären das Finden von Fehlern in Docker-Images, aber auch statische Code-Analyse mit Sicherheitsfokus. Man kann auch APIs und Webanwendungen mit Analysetools "angreifen", um hier zumindest die offensichtlichsten Schwachstellen zu erkennen, bevor sie ein echter Angreifer findet. Sämtliche Analysewerkzeuge stehen natürlich auch bösartigen Angreifern zur Verfügung, daher hilft es, schon mal vorher die eigene Anwendung auf Herz und Nieren zu prüfen. Es gibt natürlich nie eine vollständige Sicherheit, man kann (und sollte) allerdings dafür sorgen, wenigstens die offensichtlichste Angriffsfläche zu verringern.

Neben der Suche nach Fehlern müssen vor allem auch Prozesse geschaffen werden, wie die gefundenen Fehler bzw. Probleme zeitnah bearbeitet werden können.

Probleme und Werkzeuge zur Behebung

In moderner Software wird stark auf Modularisierung und Wiederverwendbarkeit gesetzt, fast alle modernen Sprachen und Frameworks setzen auf die Verwendung von Bibliotheken. Vor allem im Open-Source-Bereich ist es Standard, öffentliche Bibliotheken und deren Funktionen in die eigene Software einzubinden, um sich auf die eigene Funktionalität fokussieren zu können. Im Beispiel von Spring Boot, dem aktuell verbreitetsten Java-Framework, werden beim einfachsten Demoprojekt, das man auf der offiziellen Webseite generieren kann, knapp 50 Bibliotheken eingebunden und aus dem Internet heruntergeladen [1].

Neben diesen Standard-Bibliotheken werden aber im Laufe der Entwicklung einer Anwendung schnell viele weitere für zahlreiche Funktionen eingebunden. Mit der Menge dieser Abhängigkeiten steigt leider auch das Risiko, fehlerhafte Software in die eigene Anwendung mit zu paketieren. Bei einem größeren Projekt kommt man hier schnell auf dreistellige Zahlen. Neben möglichen Fehlverhalten ist es hierdurch auch möglich, sicherheitskritische Fehler zu importieren.

Da es einem Entwickler aber aufgrund der Menge an genutzten Bibliotheken oft nicht mehr möglich ist, ständig alle Abhängigkeiten zu prüfen, sollte man hier auf Werkzeuge zurückgreifen, die das automatisiert können. Das amerikanische NIST bietet hierzu eine öffentliche Datenbank an, in der bekannte Sicherheitslücken veröffentlicht werden [2]. Diese Schwachstellen werden von Sicherheitsfirmen, aber auch Privatpersonen und globalen IT-Konzernen gefunden, dokumentiert, geprüft und der Allgemeinheit zur Verfügung gestellt.

Eine Vielzahl von Tools kann diese Datenbank auslesen, und die eigenen Abhängigkeiten gegen die dort hinterlegten CVE-Dokumente (Common Vulnerability Disclosure and Exposure) prüfen. Ein CVE enthält zu einer bekannten Schwachstelle unter anderem folgende Daten:

  • Eindeutige ID (Jahr der Veröffentlichung und Nummer, z. B. CVE-2017-9805),
  • Beschreibung der Schwachstelle einer Bibliothek, mit Angabe der Version,
  • Kennzahlen zur Einschätzung der Gefährdung, z. B. "10.0 CRITICAL",
  • Kennzahlen zu Wahrscheinlichkeit der Schwachstelle und weitere.

Als Beispiel mit Java soll im Folgenden das Tool Dependency Check gezeigt werden [3]. Es ist Open Source, wird aktiv maintaint, von der OWASP Foundation unterstützt und ist ohne Lizenzkosten verfügbar [4]. Neben Dependency Check gibt es aber zahlreiche andere, auch kommerzielle Tools und Frameworks verschiedener Anbieter, mit verschiedenem Umfang und Lizenzmodellen.

Dependency Check unterstützt neben Java auch .NET, Ruby, nodeJS und Python. Für Java stehen u. a. Plugins für Maven und Gradle bereit. So lässt sich ein einfacher Check mit Maven z. B. folgendermaßen ausführen:

mvn org.owasp:dependency-check-maven:6.1.5:check

Über das Plugin wird zunächst eine lokale Datenbank erstellt, in der die Schwachstellendaten aus der NIST-Datenbank abgelegt werden. Beim ersten Lauf kann das etwas länger dauern, bei folgenden Durchläufen werden dann nur noch Änderungsdaten eingefügt. Bei der Nutzung in Continuous Integration bzw. Build-Systemen empfiehlt es sich, die Datenbank lokal zu cachen oder einen Mirror vorzuhalten.

Im Beispiel von Maven findet sich die Datenbank im lokalen Repository, standardmäßig im Benutzerverzeichnis unter

~/.m2/repository/org/owasp/dependency-check-data

Der eigentliche Check vergleicht die Inhalte der pom.xml, in der in Maven die Dependencies verwaltet werden, mit den bekannten Schwachstellen aus der Datenbank. Die Datenbank kennt zu jeder eingebundenen Bibliothek (und deren transitiven Abhängigkeiten) alle bekannten Schwachstellen in Verbindung mit der jeweiligen Version.

Folgender Test wurde mit einem "nackten" Spring-Boot-Projekt gestartet, generiert auf https://start.spring.io mit Version 2.2.13.RELEASE (die aktuellste 2.2). Diese Version wird zwar noch offiziell unterstützt, es finden sich trotzdem Abhängigkeiten, die potentielle Sicherheitsrisiken bergen.

Auf der Console wird zunächst ein einfacher Output mit den gefundenen Schwachstellen dokumentiert (s. Abb. 1).

Für entsprechende Funde wird auch ein HTML-Report generiert, um die Schwachstellen ausführlicher zu dokumentieren (s. Abb. 2),

Der Report bildet vor allem die Informationen aus der CVE ab.

Für jede Schwachstelle gibt es hier eine sprechende Beschreibung, die man auch ohne große Sicherheitskenntnisse verstehen kann. In der Regel hat man ja als Entwickler mindestens eine ungefähre Vorstellung, was in den entsprechend eingebundenen Bibliotheken passiert, und kann damit einschätzen, ob man die entsprechende Schwachstelle überhaupt benutzt, und wie man sie möglicherweise umgehen kann, falls es kein Update gibt. Es kommt leider vor, dass Fehler vom Autor oder Maintainer einer Bibliothek nur langsam oder nie behoben werden. Je nach Schwachstelle benötigt man möglicherweise einen Workaround im eigenen Teil der Software oder vielleicht auch in der Betriebsinfrastruktur, z. B. indem man einen schädlichen Request an einer Firewall blockiert.

Neben der Beschreibung enthält der Report auch verschiedene Kennzahlen, die sich u. a. aus dem Grad der Schwachstelle und ihrer Wahrscheinlichkeit zusammensetzen. Als Sicherheitsneuling bekommt man hier vor allem durch die "Base Score", die einen Wert von 0 (LOW) bis 10 (CRITICAL) annehmen kann, einen Eindruck, wie dringlich die Schwachstelle zu behandeln ist. Hier muss man allerdings beachten, dass es Kombinationen von weniger hohen Schwachstellen gibt, die zusammen eine höhere Gefährdung darstellen, als der Wert zunächst annehmen lässt.

Weiterhin sind im Report in der Regel Referenzen aufgeführt, unter denen man mehr Information zu den gefundenen Schwachstellen recherchieren kann. Hier findet man in der Regel Einträge von Mailinglisten der Hersteller, Einträge in Issue-Trackern, aber auch Blogposts, die sich tiefer mit den Problemen befassen. Diese Informationen bieten eine großartige Quelle, mit denen man sein Sicherheits- aber auch das allgemeine Entwicklungswissen erweitern kann. Oft ist hier beschrieben, wie die Lücke gefunden wurde, und man kann auch diese Information in Zukunft nutzen, um die eigene Software zu prüfen. Man lernt quasi während der Problemsuche bzw. -analyse automatisch dazu. Zusätzlich werden für einige Fehler auch "Proof-of-Concept"-Snippets verlinkt, also Code, mit dem man die Schwachstelle selbst angreifen kann, um so festzustellen, ob die eigene Anwendung tatsächlich betroffen ist. Dieser Beispielcode kann natürlich auch eine Bewertungsgrundlage oder ein Argument liefern, wie schnell man die Probleme beheben sollte. Zusätzlich zum Report kann man Dependency Check so konfigurieren, dass je nach gefundener Schwachstellen der Build fehlschlägt.

Für diese Entscheidung ist vor allem der Prozess des jeweiligen Entwicklungsteams wichtig. Möglicherweise behindert man sich mit einem fehlschlagenden Job einer Entwicklungspipeline selbst. Dies könnte z. B. der Fall sein, wenn eine Schwachstelle existiert, für die noch kein Fix bereitsteht. Empfehlenswert(er) ist es hier, bei gefundenen Problemen eine Nachricht mit einer Warnung zu verschicken oder möglicherweise sogar direkt automatisiert ein entsprechendes Issue oder Ticket mit dem jeweiligen Problem zu eröffnen.

Natürlich sollten Tests auf Schwachstellen wie alle anderen Tests möglich automatisiert laufen. Im Beispiel von Dependency Checkkann man den o. g. Maven-Befehl einfach als Script in jeglichen Continuous-Integration-Tools ausführen. Manche Frameworks wie z. B. Jenkins bieten aber auch Plugins, die den Test samt Auswertung übernehmen.

Wenn die lokale Schwachstellendatenbank bereits vorhanden ist, dauert ein Testlauf nur wenige Sekunden und sollte in keiner Pipeline fehlen. Falls in einem Projekt länger keine Weiterentwicklung und damit auch keine regelmäßigen CI-Builds laufen, sollte man einen regelmäßigen Test implementieren, beispielsweise einmal am Tag oder einmal pro Woche, um keine Schwachstellen zu "verpassen".

Wie bei allen Tools gibt es auch bei Sicherheits-Frameworks Fehlermeldungen, die fälschlicherweise gefunden werden, sogenannte "false positives". Dies kann passieren, weil eine Bibliothek z. B. erst kürzlich repariert wurde und der Maintainer dies noch nicht an die zentrale Datenbank gemeldet hat, oder die Änderung noch einem Review-Prozess unterliegt. Außerdem kann es passieren, dass eine Schwachstelle zwar existiert, man aber den entsprechenden Teil der Bibliothek gar nicht benutzt. Im Beispiel des oben gezeigten Reports sieht man z. B. an der Beschreibung, dass sich die Schwachstelle nur auf SMTPS-Protokoll beschränkt, also wohl etwas mit E-Mail-Versand zu tun hat. Benutzt man diese Funktionalität nicht, kann man die Schwachstelle wohl getrost ignorieren. Kommerzielle Security-Tools werben hier mit einer besseren Qualität der Tests, so dass man als Nutzer weniger Aufwand betreiben muss.

Um diese irrelevanten Ergebnisse zu unterdrücken, kann man bei Dependency Check eine "Suppression"-Liste führen, mit der die entsprechende Bibliothek nicht mehr im Report auftaucht und nicht beachtet wird. Der oben gezeigte HTML-Report bietet zu jeder Schwachstelle bereits entsprechende XML-Snippets, die man einfach kopieren kann, z. B. in eine suppression.xml-Datei, die man dann im Test bzw. dem Plugin konfiguriert.

Neben der Kombination aus CVE und dem Paketnamen der Bibliothek kann man hier u. a. auch ein Ablaufdatum für jede Suppression konfigurieren, um eine Art Wiedervorlage zu erzwingen. So kann man beispielsweise Schwachstellen ausblenden, zu denen aktuell kein Update möglich ist, die man aber nicht vergessen möchte. Im Fall des o. g. Beispiels wäre es möglich, dass später im Projektverlauf durchaus E-Mail-Versand angeboten wird. Dann würde die Schwachstelle durchaus interessanter.
Jegliche Suppressions sollten sinnvoll dokumentiert werden. Im Idealfall wird die Suppression-Datei in git direkt im Projekt abgelegt und mit einer Aussagekräftigen commit-Message versehen, so dass jederzeit nachvollzogen werden kann, warum genau diese Schwachstelle ignoriert wird.

Reaktion und Problembehebung

Nachdem die Informationen über mögliche Schwachstellen nun bekannt sind, muss man sich als Team über den Prozess Gedanken machen, wie man sie behebt. Hierzu muss man nicht erst Kanban einführen oder über einen funktionierenden Scrum-Prozess verfügen, man sollte aber kurzfristig eine Einschätzung treffen, ob die eigene Anwendung betroffen ist und wie schnell man reagieren muss. Es hilft, wenn man als Entwicklungsteam festlegt, wer sich als erstes um neu gefundene Probleme kümmert. Idealerweise teilt sich das Team diese Aufgabe auf, so dass hier keine Wissenslücken entstehen.

Sicherheitslücken sollten wie technische Schulden behandelt und zeitnah behoben werden. Wie auch bei sonstigen Upgrades gilt neben dem Risiko eines Angriffs: je länger man eine veraltete Version im Code beibehält, desto größer wird das Risiko, dass ein Update nicht mehr ohne weiteres kompatibel ist (bezüglich Funktionsumfang, unabhängig vom Sicherheitsaspekt).

Um Aufwände, die grundlegend durch Library-Updates entstehen, zu minimieren, kann man diese auch über Automatismen durchführen lassen. Tools wie z. B. Renovate Bot prüfen vorhandene Dependencies auf mögliche Updates und erstellen automatisiert Merge- oder Pull-Requests mit einer neueren Version [5]. Renovate wird hierzu regelmäßig als eigenständige Anwendung gestartet. Man vergibt dazu einen API-Key für das zu prüfende git-Repository, mit dem Pull-Requests angelegt werden können.

In Verbindung mit hoher Testabdeckung und Vertrauen in die eigene Continuous-Integration-Lösung kann man so regelmäßig automatisch Updates vornehmen, die komplett ohne manuellen Aufwand durchgeführt werden. Dazu stellt man die Pipeline der zu testenden Anwendung so auf, dass bei Merge-Requests mit Versions-Upgrades, die von Renovate erstellt wurden und die alle automatisierten Tests bestanden haben, automatisch gemerged wird. Hostet man seinen Code bei Github, kann man ein ähnliches Feature, Dependabot, für Open Source-Projekte kostenfrei nutzen. Github scannt dann in regelmäßigen Abständen vorhandene Repositories und benachrichtigt den Besitzer, falls Schwachstellen gefunden oder Updates als Pull-Request angelegt wurden.

Transparenz

In manchen Organisationsstrukturen macht es Sinn, Ergebnisse von Sicherheitsscans über Produkt- und Teamgrenzen hinweg darzustellen. So könnte je nach Vorgabe z. B. ein Sicherheits- oder QA-Beauftragter Zugriff auf die entsprechenden Ergebnisse verlangen. Hier empfiehlt sich eine Lösung, die zentral die Sicherheits-Testergebnisse aller Projekte darstellt. Dependency Check kann man zu diesem Zweck mit einem zentralen Server erweitern, dem Dependency Track[6]. Dependency Track bietet eine Schnittstelle, an die man die Reports jedes Scans schicken kann. Auf dem Server werden diese Reports ausgewertet und global über alle Projekte dargestellt. Hier lassen sich hier auch Trends ablesen, z. B. wieviele Schwachstellen seit der letzten Entwicklungsiteration dazugekommen oder weggefallen sind. Zusätzlich bringt Dependency Track eine Mirror-Funktion für die NIST NVD-Datenbank mit. So kann man Traffic einsparen und gleichzeitig helfen, die öffentliche Schnittstelle nicht zu überlasten.

Aufwände

Grundlegend sollte man bei der ernst gemeinten Einführung eines Test-Tools wie Dependency Check zunächst einmal alle gefundenen Schwachstellen behandeln. Erst wenn man einen Zustand erreicht hat, in dem keine vorhandenen Schwachstellen mehr erkannt werden, macht es Sinn, den automatisierten Test und einen entsprechenden Folgeprozess einzuführen. Von einem Tool, das einem nur in jedem Build anzeigt, wie viele Fehler gefunden wurden, ohne die Möglichkeit zu haben, entsprechende Verbesserungen voranzutreiben, hat man als Entwickler keine Vorteile.

Fazit

Unabhängig von der Auswahl der Tools bietet die Umsetzung einen großen (Sicherheits-)Gewinn bei relativ geringem Aufwand. Neben der kontinuierlichen Verbesserung der Software früh in der Entwicklung bieten die meisten Werkzeuge durch ihre gute Dokumentation die Möglichkeit, neben der Absicherung auch noch einiges dazuzulernen und sich selbst als Entwickler kontinuierlich zu verbessern.

Ist DevSecOps mit Open-Source-Tooling nun gratis? Viele Tools und Frameworks sind in der Nutzung erst einmal kostenlos, verlangen also je nach Lizenz keine Gebühren. Man darf allerdings die Zeit, die man zur Umsetzung des Workflows und zur Schaffung der nachgelagerten Prozesse braucht, nicht unterschätzen. Möglicherweise kommen hier dann auch kommerzielle Produkte in Frage, die eine vereinfachte Einrichtung, automatisierte Problembehebung und Support liefern.

Autor

Christian Kühn

Christian Kühn ist Lead Developer (Point of Sale) bei dmTECH. Er beschäftigt sich mit Backend-Entwicklung, Automatisierung, Cloud- und Sicherheits-Themen.
>> Weiterlesen
Das könnte Sie auch interessieren
Kommentare (0)

Neuen Kommentar schreiben