Lückenlos sicher entwickeln mit DevSecOps

Für durchgängige Security in der Softwareentwicklung bietet DevSecOps Maßnahmen, jeden Schritt von der Entwicklung bis zum Betrieb abzusichern.

In Pocket speichern vorlesen Druckansicht 19 Kommentare lesen

(Bild: bearsky23/shutterstock.com)

Lesezeit: 10 Min.
Von
  • Nicolas Byl
Inhaltsverzeichnis

Die rasante Entwicklung von DevSecOps verwundert nicht. Immerhin geben die Tech-Giganten und Startups den Takt vor, in der die IT sich weiterentwickelt. Viele traditionelle Organisationen haben dabei Schwierigkeiten hinterherzukommen, wollen und können aber naturgemäß an Sicherheit und Compliance nicht sparen. Automatisierung verspricht ihnen, zumindest einen Teil der Bürde abzunehmen. Mit engen Feedbackschleifen können sie versuchen, die eigenen Lieferzeiten ohne Abstriche bei der Sicherheit zu verkürzen.

Viele Organisationen verfolgen in ihrer Sicherheitsstrategie aber immer noch einen Ansatz, den man als die trügerische Sicherheit einer Burg bezeichnen kann. Oft gilt einfach alles per Definition als sicher, was innerhalb des eigenen Netzes geschieht. Analog zu Burgherren versuchen die Unternehmen ausschließlich, die umgebenden Mauern immer höher zu ziehen. Allerdings ist die Grundannahme inzwischen falsch, da im übertragenen Sinne das Burgtor ständig offensteht. In der vernetzten Welt gibt es eine kontinuierliche Kommunikation mit Kunden und Partnern, und die eigenen Mitarbeiterinnen und Mitarbeiter haben ihren Arbeitsplatz nicht unbedingt innerhalb der Burg.

Auf dem Weg von der Entwicklung bis zu den Endusern durchläuft der Code zahlreiche Stationen (Abb. 1).

Das müssen Unternehmen bei Sicherheitsmaßnahmen entlang des Wegs beachten, den Code zurücklegt. Schließlich liegen nicht mehr zwangsläufig alle Systeme im eigenen Netzwerk oder zumindest in einem einheitlichen Rechenzentrum oder derselben Cloud. Beispielsweise kommt beim Hosting des Codes ein Managed Service wie GitHub zum Einsatz, der Build-Server steht aus historischen Gründen im Netzwerkschrank und das Deployment landet schließlich beim Cloud-Provider der Wahl.

Das Absichern von Code kann mit Blick auf den Anfang der Kette bei der Entwicklung beginnen. Dort lassen sich Maßnahmen ergreifen, die den Code bereits beim Schreiben überprüfen und unmittelbar Rückmeldung geben. Dabei ist jedoch darauf zu achten, dass sie den Entwicklerinnen und Entwicklern nicht im Wege sind. Lange Reaktionszeiten des Systems aufgrund von Tests erschweren die Bearbeitung, womit die Akzeptanz sinken kann.

Sonderheft zu sicherer Softwareentwicklung

Dieser Artikel stammt aus dem iX-Developer-Sonderheft "Sichere Software entwickeln". Es behandelt auf 156 Seiten unter anderem die Themen Web-Application-Security, Codeanalyseverfahren und Cloud-Security. Der Schwerpunkt zu DevSecOps zeigt Methoden, Werkzeuge und Reifegradmodelle auf.

Für spezifische Programmiersprachen soll ein Artikel helfen, Speicherfehler in C++ aufzuspüren und zu vermeiden, und ein weiterer zeigt die Sicherheitskonzepte von Rust auf. Wer mit Java entwickelt, findet eine Übersicht der Änderungen an der Security von Java 11 bis Java 17.

Der Themenbereich Kryptografie geht von den Grundlagen über die Fallstricke beim Einbinden kryptografischer Verfahren in eigene Anwendungen bis zum Ausblick auf die Post-Quanten-Kryptografie.

Das Heft ist im heise Shop als PDF für 12,99 Euro verfügbar. Die gedruckte Ausgabe kostet 14,90 Euro. Ein Bundle aus gedruckter Ausgabe plus PDF ist ebenfalls verfügbar.

Ein gutes Beispiel für sinnvolle Maßnahmen sind Git Hooks, mit denen sich Code beim commit prüfen lässt. Sie greifen bei jedem Versuch, Änderungen lokal in das Repository zu überführen, und können schwerwiegende Probleme bei der Implementierung und vor der Weiterverarbeitung abfangen. Ein Beispiel hierfür können hartcodierte Credentials sein, die unter keinen Umständen in das Repository und dessen History gelangen sollten. Durch das Testen an dieser Stelle ist der Kontext beim Entwickeln noch vorhanden und eine zügige Änderung möglich.

Hilfreich beim Einbinden der Git Hooks ist das Framework pre-commit. Es bietet einen Rahmen, Hooks zu erstellen und zu verteilen, ohne sich in die Untiefen der Shell-Programmierung zu begeben.

Sobald der Code erstellt ist, bietet sich vor der endgültigen Übernahme in eine Mainline oder sogar in ein Release an, Änderungen durch ein zweites Augenpaar zu prüfen. Solche Code-Reviews können als manuelle Maßnahmen verhindern, dass Mitarbeiterinnen oder Mitarbeiter beispielsweise einen Bitcoin-Miner in das Produktionssystem einbauen. Neben dem Sicherheitsaspekt kommt die Maßnahme dem Wissenstransfer im Team und der allgemeinen Qualitätskontrolle zugute.

Zur Nachvollziehbarkeit – vor allem in späteren Stationen des Codes – kann eine weitere, selten genutzte Funktion von Git dienen: das Signieren von Commits. Üblicherweise speichern Teams beim Erzeugen von Commits in Git zwar Namen und E-Mail-Adresse, prüfen aber nicht, wer die Metadaten erzeugt. Daher sollte, gerade in Umgebungen mit hohen Compliance-Anforderungen, das Signieren über GPG-Keys zwingend erforderlich sein, um eine kryptografische Validierung des Verfassers beziehungsweise der Verfasserin zu ermöglichen. USB-Sticks mit integrierter PGP-Karte wie der Yubikey können ein weiteres Sicherheitsnetz schaffen, um das Einschleusen von gefährlichem Code zu erschweren.

Die nächste Station auf der Reise des Codes ist der Build-Prozess, bei dem unter anderem aus dem Sourcecode ein lauffähiges Artefakt entsteht. Das ist eine gute Station für weitere Prüfungen auf dem Code, um die Qualität zu sichern. Zunächst lässt sich die Codebasis mit diversen Tools einer Sicherheitsanalyse unterziehen. Die Werkzeuge decken die Bereiche von der statischen Analyse des Quellcodes bis zu ausgefeilten Fuzzing-Methoden ab.

Nicht zu unterschätzen ist der Berg von Abhängigkeiten. Nur wenige Anwendungen nutzen ausschließlich die Standardbibliothek ihrer Programmiersprache. Entsprechende Dependencies innerhalb des Ökosystems werden stattdessen für Paketmanager wie npm, Maven oder pip im Projekt deklariert und während des Build-Vorgangs heruntergeladen. Damit gleicht bei vielen aktuellen Frameworks die vollständige Codebasis einem Eisberg: Der eigene und selbst verwaltete Code ist nur dessen kleine Spitze über der Wasseroberfläche, während sich der Großteil der gesamten Codebasis in den Abhängigkeiten versteckt. Der Trend zu Cloud und Containern hat die Tendenz verstärkt, da die Nutzung der Dienste nicht mehr manuell, sondern ebenfalls codebasiert erfolgt. Dieser Teil ist für Angreifer besonders interessant, da erfolgreiche Attacken auch jenseits der eigentlichen Anwendung Zugriff finden.

Die deklarative Form ermöglicht beim Überprüfen der Abhängigkeiten weitere Maßnahmen. Durch die Beschreibung, die beispielsweise im XML- oder JSON-Format vorliegt, kann eine automatische Prüfung der externen Dependencies im Abgleich mit einer Liste bekannter Schwachstellen erfolgen. Dazu sind mehrere Scanner erhältlich, die beispielsweise die Maven-Abhängigkeiten prüfen und beim Erkennen schwerer Sicherheitslücken die Pipeline sofort stoppen. Dass die meisten Tools nicht Open Source beziehungsweise kostenfrei, sondern kommerziell sind, ist verständlich. Schließlich ist damit neben der eigentlichen Scannersoftware ein nicht unerheblicher Aufwand bei der manuellen Pflege der Datenbank von Schwachstellen verbunden. Populäre Beispiele hierbei sind Snyk und Aqua im kommerziellen Bereich sowie clair für Docker Images als Open Source Tool.

Beim Patching ist es wichtig, mit der Automatisierung auf halber Strecke stehen zu bleiben. Nur wenn das gefährdete Artefakt ausgetauscht wird, kann ein sicherer Zustand wiederhergestellt werden (Abb. 2).

Während die Prüftechniken die Sicherheitslage durch mehr Transparenz verbessern, bleibt eine Herausforderung bestehen: Die als unsicher erkannten Dependencies durch gefixte Versionen auszutauschen. Die zeitraubende und aufwendige manuelle Arbeit haben Teams bis vor einigen Jahren häufig an Junior-Entwicklerinnen oder Praktikanten delegiert. Inzwischen haben sich einige Tools wie Dependabot und Renovate etabliert, die den finalen Schritt des Updatezyklus automatisieren. Passende Updates erhalten Entwicklerinnen und Entwickler auf eine für sie gewohnte Art, beispielsweise per Pull Request. Auf die Weise fügen sie sich in den regulären Workflow ein. Die Deklaration von Code spielt damit ihre Stärke aus.

Nachdem das Build-System ein Artefakt möglichst ohne Schwachstellen erzeugt hat, muss es in einer Form bereitgestellt werden, in der Enduser etwas damit anfangen können. Dabei lauern erneut potenzielle Schwachstellen und unsichere Zustände. Da Build- und Ausführungsumgebung häufig nicht identisch sind, sollte zunächst eine automatische Überprüfung sicherstellen, dass ein valides Artefakt vorhanden ist. Dafür lässt es sich beim Build- und Analyseprozess digital signieren. Später prüft die Ausführungsumgebung vor dem Start bei der sogenannten Binary Attestation, ob alle erforderlichen Unterschriften vorhanden sind. Auf diese Weise lässt sich ein Binary von mehreren unabhängigen Instanzen testen und als valide kennzeichnen.

Grundsätzlich sollte gelten, dass nur erfolgreich geprüfte Artefakte ausgeführt werden dürfen. Das ermöglicht eine technische Bedingung, ob die eingesetzte Software sowohl eigenen als auch regulatorischen Vorgaben genügt. Die Prüfung ermöglicht oft den Verzicht auf umfangreiche Checklisten, die bei jedem Release manuell zu prüfen sind. Allerdings ist Vorsicht geboten: Technische Regularien sind oft bewusst schwammig definiert, um sie nicht ständig anpassen zu müssen. Für eine automatische Prüfung reicht aber beispielsweise eine Definition als "Stand der Technik" nicht aus. Sie benötigt Regeln, die konkret und maschinenlesbar deklariert sind.

Häufig ist das Secrets-Management eine weitere Herausforderung beim Deployment. Das gilt zunächst für die Zugangsdaten, um die Bereitstellung zu autorisieren. Teams müssen beim Design der CI / CD-Pipeline (Continuous Integration / Continuous Delivery) den Fokus auf die Sicherheit setzen. Schließlich soll nicht zentral etwas vorhanden sein, was Schlüssel zu allen Umgebungen bereithält und nach einem kompromittierenden Angriff Zugang zu allen Systemen frei Haus bereitstellt. Als erster Schritt bietet sich die Trennung von Zugängen auf der Ebene der Anwendungen oder Teams an. Eine Alternative ist das agentenbasierte Deployment: Ein Agent läuft in der Zielumgebung und erhält über die IAM-Mechanismen (Identity and Access Management) des Zielsystems die Berechtigung, es von innen zu bestücken. Das Vorgehen vermeidet eine allzu große Konzentration der Zugangsdaten an einer Stelle der Landschaft.

Nach der erfolgreichen Bereitstellung steht für die Anwendung ein sicherer Betrieb im Fokus. Als einer der ersten Schritte bietet sich an, die verwendeten Artefakte nicht nur beim Build auf bekannte Schwachstellen zu prüfen, sondern die Analyse regelmäßig zu wiederholen. Auf diese Weise können Unternehmen auf später bekannt gewordene Schwachstellen reagieren.

Ein weiterer Punkt ist die Kontrolle der laufenden Software, für die als Minimalanforderung alle Regeln der Kunst des klassischen Betriebs gelten. Die Automatisierung ermöglicht weitergehende Schritte. Über Policy-basierte Systeme wie den Open Policy Agent lässt sich sicherstellen, dass Anwendungen von vorneherein nur eine Untermenge der Rechte in der Laufzeitumgebung erhalten. Darüber hinaus lassen sich zusätzliche Maßnahmen wie die durchgehende Verschlüsselung aller Datenspeicher erzwingen.

Als letzter Punkt gilt, dass man immer mit Fehlern rechnen sollte und auf sie vorbereitet sein muss. Sinnvoll sind Übungen, um Abläufe für den Notfall zu proben. Dazu gehört regelmäßig zu prüfen, ob alle im Team wissen, wo im Krisenfall der Treffpunkt ist. Daneben muss sichergestellt sein, dass alle Zugänge zu den Systemen vorhanden sind. Zum einen spart die Vorsorge im Ernstfall viel Zeit und zum anderen geben regelmäßig geübte Vorgänge allen Beteiligten Sicherheit beim Eintreten einer unbekannten Situation.

DevSecOps ist ein Querschnitt vieler Themen vom Entwickeln bis zum Betrieb von Software. Ein konsequentes Nachverfolgen des Codes kann deutlich dabei helfen, Maßnahmen zu identifizieren, um die eigene Sicherheitslage zu verbessern. Auf den ersten Blick mag das aufgrund der vielschichtigen Komplexität wie ein unüberwindlicher Berg aussehen. In dem Fall hilft es, die Konzentration weg vom großen Ganzen auf den jeweils nächsten Schritt zu lenken.

Nicolas Byl
sammelte bereits während des Studiums der Medizinischen Informatik erste Erfahrungen mit Java-basierten Webportalen und entdeckte seine Leidenschaft für verteilte Systeme. Bei der codecentric AG beschäftigt er sich mit skalierbaren Cloud-nativen Infrastrukturen für die Applikationsentwicklung und die Verarbeitung von Datenströmen.
(rme)