zurück zum Artikel

Automatisierte Security-Tests innerhalb der Continuous-Integration-Pipeline

Benjamin Schnarr, Andreas Vetterlein, Shushant Kakkar
Automatisierte Security-Tests innerhalb der Continuous-Integration-Pipeline

Die Anzahl der Angriffe auf unsichere Webanwendungen steigt stetig. Daher lohnt ein Blick darauf, wie sich Sicherheitslücken automatisiert in Anwendungen finden lassen, bevor die Software veröffentlicht wird.

Es ist das Jahr von Spectre und Meltdown – Angriffsszenarien, die in der Vergangenheit tausende Systeme durch das gezielte Ausnutzen von Sicherheitslücken erfolgreich attackiert haben. Häufig nutzen Angriffe Sicherheitslücken auf Hardware-, Netzwerk- oder Betriebssystemebene aus, um unautorisierten Zugriff auf Systeme zu erlangen.

Darüber hinaus sind insbesondere Webanwendungen, wie Webshops oder Buchungssysteme, beliebte Angriffsziele, da sich oftmals durch mangelnde Sicherheitsmechanismen Kunden- und Zahlungsdaten abgreifen lassen. Für den Angriff auf Webanwendungen kommen unterschiedliche Angriffsvektoren infrage. Beliebte Methoden sind beispielsweise das Ausnutzen unzureichender Verschlüsselungsmechanismen (was die Tür für Zero Day Exploits wie Heartbleed öffnet) und browserbasierte Angriffe, beispielsweise Cross-Site-Scripting-Attacken. Frameworks wie Metasploit bieten darüber hinaus ein ganzes Set an Tools, um gängige Angriffe automatisiert durchzuführen.

Oftmals liegt der Fokus bei der Entwicklung von Webanwendungen auf neuen Features statt auf Security, sodass Sicherheitslücken zum Teil erst im produktiven Betrieb gefunden werden. Daher erhalten gezielte Analysen zum Auffinden von Sicherheitslücken einen immer größeren Stellenwert innerhalb des Softwareentwicklungsprozesses. Zur systematischen Identifikation von Sicherheitslücken in Webanwendungen kommen in der Regel statische und dynamische Anwendungsanalysen zum Einsatz.

Statische Analysen (Static Application Security Testing, kurz SAST) untersuchen den Quellcode sowie die verwendeten Frameworks, um Sicherheitslücken und Anti-Patterns innerhalb der Anwendung zu identifizieren. Insbesondere der Einsatz unsicherer Open-Source-Frameworks eröffnet zahlreiche Möglichkeiten, da oftmals Lücken einer bestimmten Framework-Version im Netz veröffentlicht sind. Durch regelmäßige statische Codeanalysen lassen sich solche Schwachstellen in der eigenen Applikation frühzeitig finden.

Dynamische Anwendungsanalysen (Dynamic Application Security Testing, kurz DAST) untersuchen darüber hinaus die jeweilige Anwendung zur Laufzeit, indem sie gezielt bösartige Anfragen zur Anwendung schicken. Die Antworten der Applikation werden dann ausgewertet, um daraus mögliche Rückschlüsse auf Sicherheitslücken zu ziehen. Dadurch lassen sich Angriffsvektoren identifizieren, die nicht direkt aus dem Quellcode ersichtlich sind.

Zur frühzeitigen Erkennung von Sicherheitslücken bietet es sich an, die jeweiligen Sicherheitsanalysen direkt in den Continuous-Integration- und Continuous-Delivery-Prozess (CI/CD) der Anwendung zu integrieren. Konkret kann das bedeuten, eingesetzte Analysetools als Build-Job innerhalb eines Build-Servers wie Jenkins oder Gitlab CI auszuführen.

Abhängig von den Analyseergebnissen ist eine regelbasierte Entscheidung darüber möglich, ob der Build-Job erfolgreich durchläuft oder aufgrund von identifizierten Sicherheitslücken als fehlerhaft markiert wird. Insbesondere die dynamischen Analysen sind hierbei von Bedeutung, da sie das konkrete Laufzeitverhalten der Anwendung fokussieren und daher die gleiche Sicht auf die Anwendung haben wie ein potenzieller Angreifer.

Als Richtlinie zur Integration von Security-Checks in die eigene CI/CD-Pipeline können Entwickler das Reifegradmodell Security DevOps Maturity Model (SDOMM) verwenden. Es dient dazu, eine Roadmap für Projekte zu definieren, um ein bestimmtes Automatisierungslevel für Sicherheitsanalysen zu erreichen. Das Modell definiert hierzu vier Achsen mit jeweils vier erreichbaren Level. Je höher das erreichte Level innerhalb einer Achse ist, desto höher ist der Grad an Automation. Für jedes Level definiert das Reifegradmodell Maßnahmen, die zum Erreichen des jeweiligen Levels umzusetzen sind (wie das Durchführen dynamischer Anwendungsanalysen nach erfolgreicher Authentifizierung an der Anwendung).

Die erste Achse des Reifegradmodells bezieht sich auf die dynamischen Anwendungsanalysen ("Dynamic Depth"). Über die Achse "Static Depth" wird der Reifegrad der statischen Anwendungsanalysen gemessen. Weiterhin misst die Achse "Intensity" die Stärke der durchgeführten Analysen. Die Achse "Consolidation" erlaubt es, Rückschlüsse darauf zu ziehen, wie mit den identifizierten Ergebnissen im Nachgang umgegangen wird. Je nachdem, welcher Reifegrad im konkreten Anwendungsfall als Ziel gesetzt ist, lassen sich anhand dieser Einteilung konkrete Handlungsempfehlungen ableiten.

Das Security DevOps Maturity Model definiert über Achsen und Level unterschiedliche Reifegradstufen für Sicherheitsanalysen in der CI/CD-Kette.

Das Security DevOps Maturity Model definiert über Achsen und Level unterschiedliche Reifegradstufen für Sicherheitsanalysen in der CI/CD-Kette.

Zum Durchführen statischer Codeanalysen existieren viele kommerzielle und freie Werkzeuge am Markt. Einige sind als Plug-in direkt in die eigene Entwicklungsumgebung integrierbar, sodass Entwickler bereits während der Implementierung Rückmeldungen zu potenziellen Sicherheitslücken erhalten. Darüber hinaus lassen sich die Analysetools in der Regel über Maven oder Gradle in den CI/CD-Prozess der Anwendung integrieren, sodass ein Build-Server wie Jenkins die Analysen automatisiert ausführt. Die generierten Reports stehen anschließend als HTML- oder XML-Dateien zur Verfügung.

Die meisten Webanwendungen verwenden Frameworks und Drittanbieter-Bibliotheken, um den Entwicklungsprozess zu vereinfachen und zu beschleunigen. Doch auch diese können schwerwiegende Sicherheitslücken enthalten. Mit dem OWASP Dependency Check können Entwickler die verwendeten Abhängigkeiten auf bekannte Schwachstellen prüfen, ohne den kompletten Code der jeweiligen Bibliothek explizit scannen zu müssen. Hierfür ermittelt der Dependency Check die jeweilige Version der verwendeten Bibliothek und prüft diese gegen die National Vulnerability Database (NVD) des NIST. Diese Datenbank sammelt und bewertet alle veröffentlichten Sicherheitslücken zahlreicher Frameworks und Bibliotheken. Als Ergebnis erzeugt der OWASP Dependency Check einen Report, der die anfälligen Abhängigkeiten inklusive CVE-Code (Common Vulnerabilities and Exposures) auflistet. Entwickler können im Anschluss bei Bedarf über den eindeutigen CVE-Code weitere Informationen über den Angriffsvektor einholen und entsprechend reagieren. Um den OWASP Dependency Check automatisiert innerhalb eines Build-Servers auszuführen, kann ein eigener Gradle-Task innerhalb der build.gradle-Datei der Anwendung erstellt werden.

apply plugin: 'org.owasp.dependencycheck'

dependencyCheck {
autoUpdate=true
cveValidForHours=24
format='HTML'
}

Die oberen Zeilen binden das entsprechende Gradle-Plug-in ein und definieren den Gradle Task dependencyCheck, den der Build-Server im Anschluss ausführen kann. Der Konfigurationswert cveValidForHours gibt an, wie lange die lokale Kopie der NVD gültig sein soll und wann sie über das Internet automatisch aktualisiert wird.

Die Verwendung des OWASP Dependency Checks hätte den Angriff auf Equifax 2017, der einen Schaden von mehreren Hundert-Millionen Dollar verursachte, verhindern können. Die Angreifer nutzten eine bekannte Sicherheitslücke im Apache-Struts-Framework, das Equifax verwendete. Mit dem OWASP Dependency Check wäre diese Sicherheitslücke frühzeitig in den Reports aufgefallen.

Neben Frameworks und Drittanbieter-Bibliotheken ist auch eine Analyse und Prüfung der eigenen Codebasis sinnvoll. Das geht beispielsweise mit der Software PMD. PMD ist ein Codeanalysewerkzeug, das mit unterschiedlichen Programmiersprachen umgehen kann und den Quellcode nach Bugs, nicht verwendeten Elementen und Codeduplikaten durchsucht. Durch diese Prüfung lässt sich somit eine erste Qualitätsanalyse des Quellcodes durchführen.

Die Überprüfung von Codequalität ist ein wichtiger Bestandteil der Software- und damit auch der Sicherheitsanalyse. Unsauberer Code ist schlecht wartbar und führt schnell zu ungewolltem Verhalten der Anwendung. Dazu gehören auch Sicherheitslücken, die durch duplizierten Code oder komplexe Programmierung leicht zu übersehen sind. Die Regeln, nach denen PMD den Quellcode überprüft, sind vielseitig konfigurierbar. Dabei gibt es verschiedene Kategorien, unter anderem Best Practices, Code Style, Multithreading und Security.

Eine Referenzierung kann jede Regel ein- oder ausschalten. Zum Beispiel kann PMD alle Stellen im Code identifizieren, die Stacktraces ausgeben. Ist die PMD-Regel AvoidPrintStackTrace eingeschaltet, wertet PMD alle Ausgaben eines Stacktraces als Regelverstoß. Ähnlich wie der OWASP Dependency Check können Entwickler PMD über Gradle in den Build-Prozess integrieren und konfigurieren.

apply plugin: 'pmd'

pmd {
toolVersion = '5.5.5'
ignoreFailures = false
sourceSets = [sourceSets.main]
reportsDir = file("$project.buildDir/reports/pmd")
ruleSets = ["java-basic", "java-braces", "java-j2ee", "java-naming", "java-imports", "java-sunsecure", "java-unusedcode", "java-unnecessary","java-codesize"]
rulePriority = 3
}

Die Konfiguration definiert unter anderem das Ziel für den PMD-Report (reportsDir) sowie die anzuwendenden Validierungsregeln (ruleSets). Alternativ zu generellen Rule Sets lässt sich eine XML-Datei angegeben, in der man Regeln granular konfigurieren kann. Jeder Regel ist dabei eine Priorität zwischen 1 (Change absolutely required) und 5 (Change highly optional) zugeordnet. Das Attribut rulePriority legt fest, ab welcher Prioritätsstufe die Findings den Weg in den Report finden. Bei einer rulePriority von 3 sind beispielsweise die Findings aus Rule Sets der Stufen 4 und 5 nicht Teil des Reports.

Ist PMD in den Build-Prozess der Anwendung eingebunden, können Regelverstöße eines PMD-Reports bei Bedarf den aktuellen Build-Vorgang abbrechen. Hierzu dient der Konfigurationswert ignoreFailures. Das verhindert das Bereitstellen fehlerhafter oder unsicherer Softwareartefakte. Entwickler können anschließend den von PMD erstellten Report analysieren, um konkrete Maßnahmen zum Beheben der Findings abzuleiten. Bei Bedarf kann das Jenkins-Plug-in "HTML Publisher" alle erstellten Reports direkt innerhalb der Jenkins-Weboberfläche zusammentragen und anzeigen.

FindBugs stellt eine Alternative zu PMD dar und wertet bei der statischen Codeanalyse ähnliche Regelsets aus. Die Erweiterung FindSecurityBugs hingegen ist auf Sicherheitslücken spezialisiert und findet potenzielle Sicherheitsrisiken im Programmcode. Beispielsweise schlägt das Tool Alarm, wenn es mögliche Ansätze für einen Path-Traversal-Angriff oder Indikatoren für Code Injection findet. Darüber hinaus analysiert FindSecurityBugs die Implementierung von Verschlüsselungen und schlägt Alarm, wenn diese fehlerhaft sind. Folgendes Beispiel verwendet einen statischen Initialisierungsvektor, der die Verschlüsselung deutlich schwächt. FindSecurityBugs stuft dieses Finding innerhalb des Reports mit der Priorität "High" ein.

private static byte[] IV = new byte[16] {(byte)0,(byte)1,(byte)2,[...]};

public void encrypt(String message) throws Exception {

IvParameterSpec ivSpec = new IvParameterSpec(IV);
...

Analog zu den Reports von OWASP Dependency Check oder PMD können die erstellten Protokolle von FindSecurityBugs über die Jenkins Weboberfläche zur Verfügung gestellt werden, sodass Entwickler im Anschluss konkrete Maßnahmen zur Mitigation der Sicherheitslücken ableiten können. FindSecurityBugs lässt sich ebenfalls über Gradle in den CI/CD-Prozess der Anwendung integrieren. Dort kann man außerdem festlegen, bei welchen Findings der Build-Prozess kontrolliert stoppt und welche Fehlerkategorien bei der Analyse zu vernachlässigen sind.

Die Reports dieser Tools helfen somit, in unterschiedlichen Bereichen der Applikation einen Überblick über den Code und dessen Qualität zu gewinnen. Dadurch können Entwickler kontinuierlich an der Softwarequalität arbeiten und offensichtliche Sicherheitslücken vor dem Produktivbetrieb der Anwendung eliminieren.

Weiterhin erhalten Entwickler nach jedem Commit ein direktes Feedback bezüglich der Anwendungssicherheit. Neben der Integration in einen Build-Server können geeignete SAST-Analysen innerhalb der lokalen Entwicklungsumgebung verwendet werden, sodass die Codeanalysen ab Projektstart möglich sind.

Viele Sicherheitslücken lassen sich nur zur Laufzeit entdecken. Daher bedarf es neben den statischen Analysen einer Fokussierung auf das Laufzeitverhalten der Webanwendung. Das macht einen professionellen Pen-Test unumgänglich. Ein solcher Test ist häufig eine Kombination aus Analysetools und manuellen Tests, um Schwachstellen aufzudecken. Während ein realer Angreifer häufig nur eine einzige Schwachstelle zu finden und auszunutzen braucht, müssen Penetration-Tester die Webapplikation auf alle gängigen Angriffsvektoren überprüfen. Je nach Komplexität der Webanwendung und Zeitaufwand des Testers kann ein Pen-Test durchaus aufwendig sein. Tools zur dynamischen Anwendungsanalyse helfen, die Webanwendung vor den manuellen Pen-Tests auf Schwachstellen zu prüfen und abzusichern.

Die meisten Tools sind sowohl in einer kommerziellen als auch in einer frei erhältlichen Variante verfügbar. Unter den frei erhältlichen Tools ist vor allem OWASP ZAP (Zed Attack Proxy) beliebt. Das Werkzeug ermöglicht das Durchführen automatisierter Sicherheitsscans und unterstützt insbesondere dynamische Anwendungsanalysen. Darüber hinaus enthält ZAP eine Vielzahl zusätzlicher Komponenten für manuelle Scans, Proxyfunktionalitäten für Man-in-the-Middle-Angriffe und vorgefertigte Wörterbuchlisten für Fuzzer-Angriffe. Zur Durchführung dynamischer Anwendungsanalysen kommen vor allem folgende Komponenten zum Einsatz:

Spider Rekursiver: Scan der Webanwendung zum Erfassen aller Ressourcen (URLs). Automated Scanner: Aktives Angreifen der Webanwendung basierend auf bekannten Sicherheitslücken. Forced Browsing: Ausführung aller Requests im Kontext eines bestimmten Nutzers.

Durch diese Komponenten ist ZAP in der Lage, gängige Sicherheitslücken mit der Analyse des Request/Response-Verhaltens der Webanwendung zu identifizieren.

OWASP ZAP unterscheidet bei den dynamischen Analysen zwischen Pre-Auth-Scans und Post-Auth-Scans. Erstere schicken Requests an die Webanwendung, ohne an dieser authentifiziert zu sein. Bei Post-Auth-Scans findet vor den eigentlichen Analysen eine Authentifizierung statt, sodass man die Webanwendung innerhalb eines bestimmten Nutzerkontextes testen kann. Zur Konfiguration der Einstellungen bietet OWASP ZAP das Tool ZAP UI an, das auf einem Desktoprechner lokal installiert wird. Über die Oberfläche kann man festlegen, welche Angriffe OWASP ZAP in welcher Intensität (attack strength/threshold) ausführt. Neben den Pre-Auth- und Post-Auth-Scans lassen sich weitere Angriffsmuster konfigurieren, beispielsweise Fuzzer-Angriffe.

Damit OWASP ZAP auch geschützte Anwendungsbereiche analysieren kann, unterstützt das Tool standardmäßig unterschiedliche Authentifizierungsverfahren, beispielsweise formularbasierte Anmeldungen. Hierfür ist die URL der Log-in-Maske sowie der Body für den Log-in POST-Request in der ZAP-Konfiguration anzugeben. Über einen Log-in- und einen Log-out-Indicator erkennt OWASP ZAP darüber hinaus, ob ein Log-in erfolgreich war oder nicht. Im Anschluss sind alle Scans im Kontext des angegebenen Nutzers durchführbar.

Automatisierte Security-Tests innerhalb der Continuous-Integration-Pipeline

Die Maske teilt OWASP ZAP alle Informationen für eine automatisierte Authentifizierung an der jeweiligen Webanwendung mit.

Sobald alle OWASP-ZAP-Konfigurationen erstellt sind, können Entwickler sie mit einem ZAP Daemon innerhalb von ZAP UI testen. Über die Oberfläche lässt sich der Daemon direkt starten. Im Anschluss führt er die eingestellten Angriffsmuster aus. Innerhalb der Oberfläche zeigt OWASP ZAP die ermittelten Sicherheitswarnungen direkt an. Diese sind im Anschluss individuell zu prüfen, um False Positives zu vermeiden.

OWASP ZAP ist grunsätzlich in zwei Modi ausführbar: Zum einen als lokale Desktopinstallation und zum anderen als Daemon auf einem Server. Somit ist es möglich, OWASP ZAP über einen Build-Server wie Jenkins parametrisiert zu starten und automatisiert dynamische Sicherheitsscans gegen die laufende Anwendung durchzuführen. Alle zuvor in ZAP UI vorgenommenen Einstellungen können verlustfrei als Session File exportiert und einem Stand-Alone ZAP-Daemon übergeben werden, sodass dieser die konfigurierten Angriffe automatisiert und eigenständig ausführt.

Abhängig von den Analyseergebnissen kann der Deployment-Vorgang entweder kontrolliert beendet oder die Software wahlweise auf weitere Target-Server (INT/PROD) installiert werden.

Automatisierte Security-Tests innerhalb der Continuous-Integration-Pipeline

Aufruf von OWASP ZAP durch ein Shell-Skript

Nach dem Durchlauf der Analysen sind die Reports über die Jenkins-Weboberfläche verfügbar. Die von ZAP erstellten Reports listen nach Abschluss der Analysen die unterschiedlichen Schwachstellen kategorisiert nach Risikolevel auf. Die Analysen decken alle gängigen Angriffe ab. Hierzu gehören beispielsweise Szenarien in den Bereichen SQL Injection, Path Traversal und Cross-Site-Scripting. Zu beachten ist jedoch, dass es auch Sicherheitslücken gibt, die ZAP nicht ohne Weiteres findet. Beispiele hierfür sind Lücken, die durch fachliche Fehler in der Anwendungslogik entstehen.

Will man die vorgestellten Tools zur statischen und dynamischen Anwendungsanalyse in den Build-Prozess der jeweiligen Webanwendung integrieren, ergeben sich hieraus dedizierte Schritte, die der jeweilige Build-Server koordiniert ausführen muss. Die folgende Abbildung verdeutlicht den Ablauf des CI/CD-Prozesses mit den integrierten Analysen anhand einer Beispielarchitektur.

Automatisierte Security-Tests innerhalb der Continuous-Integration-Pipeline

Beispielaufbau einer Cloud-basierten CI/CD-Pipeline mit statischen und dynamischen Security-Analysen

Bei jedem Code-Commit wird der neue Code vom zentralen Jenkins-Build-Server aus der Versionsverwaltung ausgecheckt. Anschließend führen OWASP Dependency Check, PMD und FindSecurityBugs die statischen Codeanalysen durch. Innerhalb der Weboberfläche von Jenkins sammeln sich die erstellten Reports. Sofern die Analyseergebnisse den konfigurierten Richtlinien entsprechen, wird die Webanwendung paketiert, auf einem separaten Testserver installiert und ausgeführt. Anschließend startet Jenkins einen OWASP ZAP Daemon im Hintergrund. Basierend auf dem hinterlegten Session File beginnen nun die dynamischen Sicherheitsscans gegen die laufende Webanwendung. Den von ZAP erstellten Report stellt Jenkins nach dem Beenden der Analysen zur Verfügung, sodass die Entwickler die Resultate prüfen können. Abhängig von den gefundenen Issues kann bei Bedarf der Deployment-Vorgang auch an dieser Stelle automatisch stoppen, sodass keine schwerwiegenden Lücken auf Integrations- oder Produktionsanlagen gelangen.

Sollten einige der dynamischen Analysen aufgrund der konfigurierten Angriffsvektoren zu viel Zeit in Anspruch nehmen, können diese auch wahlweise nachts während eines Nightly Builds ausgeführt werden. Dadurch verkürzt sich die Build-Zeit während der täglichen Entwicklung. Gleichzeitig wird die Anwendung während des Nightly Builds ausführlich geprüft.

Die Integration statischer und dynamischer Anwendungsanalysen in die CI/CD-Pipeline und die damit einhergehende Automatisierung ist heutzutage unerlässlich. Entwickler und Architekten erhalten auf Wunsch nach jedem Code-Commit eine direkte Rückmeldung bezüglich der durchgeführten SAST- und DAST-Analysen. Regelbasierte Build-Jobs können darüber hinaus verhindern, dass unsichere Softwareartefakte in die Produktion gelangen. SAST-Tools wie OWASP Dependency Check, PMD und FindSecurityBugs sind einfach über Gradle Tasks in den CI/CD-Prozess integrierbar.

Aufgrund der vielen unterstützten Angriffsvektoren eignet sich darüber hinaus OWASP ZAP als Pen-Testing-Tool zum Ausführen dynamischer Anwendungsanalysen. Zahlreiche Sicherheitslücken können dadurch bereits während der Entwicklungsphase gefunden werden. Die als Ergebnis zur Verfügung stehenden Reports enthalten eine detaillierte Beschreibung des jeweiligen Angriffsvektors und eine Reihe von Best Practices zur Behebung der Lücken. Somit erhalten Entwickler implizit eine Security Guideline und setzen sich mit dem Thema Anwendungssicherheit bewusst auseinander. Auch die besten SAST- und DAST-Analysen können keine absolut sichere Software garantieren. Jedoch bieten sie neben den regulären Security- und Pen-Tests einen hervorragenden Ansatz, um die eigene Software während der Entwicklung ein Stück sicherer zu machen.

Security DevOps Maturity Model (SDOMM), Christian Schneider [1]

Benjamin Schnarr, Andreas Vetterlein und Shushant Kakkar
sind IT-Consultants bei der Acando GmbH. Neben Kundenprojekten beschäftigen sie sich intensiv mit IT-Security und Secure-Software-Engineering.
(bbo [2])


URL dieses Artikels:
https://www.heise.de/-4043429

Links in diesem Artikel:
[1] https://www.christian-schneider.net/SecurityDevOpsMaturityModel.html
[2] mailto:bbo@ix.de