Automatisierte Überprüfung von Sicherheitslücken in Abhängigkeiten von Java-Projekten

Sicherheit in Software-Projekten ist ein schwer zu beherrschendes Thema, wie regelmäßig in den Medien erscheinende Berichte über Datenlecks beweisen. Für Software-Entwickler ist es schwer zu überblicken, welchen Einfluss ihre tägliche Arbeit an nicht unmittelbar sicherheitsrelevanten Themen auf die Sicherheit ihrer Anwendung hat.

Der Original-Artikel aus der „Java aktuell“ steht hier als PDF zum Download bereit.

Einen Überblick über die häufigsten Makel in Punkto Sicherheit bietet die Top Ten des Open Web Application Security Project (OWASP), eine Non-Profit-Organisation, die sich zum Ziel gesetzt hat, die Sicherheit in Web zu verbessern. Hier zeigt sich zudem, dass auch nicht selbst geschriebener Code Sicherheitslücken enthalten kann. Diesen kann man jedoch mit wenigen Schritten überprüfen. Dieser Artikel zeigt, wie man mit geringem Aufwand zumindest über die bekannten Sicherheitslücken in verwendeten Abhängigkeiten von Dritten auf dem Laufenden bleibt.

Grundlage bildet die National Vulnerability Database (NVD) eine Art Datenbank für Sicherheitslücken. Sie wird gepflegt vom National Institute of Standards and Technology (NIST), eine mit der deutschen Physikalisch-Technische Bundesanstalt (PTB) vergleichbare US-amerikanische Bundesbehörde, die auch bekannte Verschlüsselungsalgorithmen wie DES und AES standardisiert.

Die NVD beinhaltet unter anderem die Schwachstellen, die im Industrie-Standard „Common Vulnerabilities and Exposures“ (CVE) erfasst werden. Die bereits erwähnte OWASP bietet eine Anwendung an, die automatisiert Jar-Dateien mit der NVD abgleicht. Dieser sogenannte „OWASP Dependency-Check“ (DChck) wurde für die Kommandozeile sowie für Ant, Maven, gradle, sbt, Jenkins und SonarQube implementiert.

Nachfolgend ein Lösungskonzept, um die Abhängigkeiten einer Java-Anwendung mithilfe von Maven und Jenkins regelmäßig auf neue Sicherheitslücken zu durchsuchen. Nach einmaliger Einrichtung sind keine weiteren Schritte notwendig. Die Information über neu gefundene Sicherheitslücken erfolgt dann per E-Mail. Dabei kann es sich entweder um neu bekannt gewordenen Sicherheitslücken in bestehenden Abhängigkeiten handeln oder um bereits bekannte Sicherheitslücken, die in neu hinzugefügten Abhängigkeiten existieren.

Erster Schritt: Maven-Plug-in ohne Konfiguration verwenden

Eine erste Überprüfung der Abhängigkeiten der eigenen Anwendung kann ohne weitere Konfiguration in wenigen Sekunden angestoßen werden, beispielsweise mittels Maven-Plug-in (MVN) durch mvn org.owasp:dependency-check-maven:1.4.0:aggregate.
Dabei gilt es zu beachten, dass „dependency-check-maven-plug-in“ zunächst eine lokale Kopie der CVE-Datenbank in Form einer H2 Datenbank herunterlädt und sie im lokalen Maven-Repository speichert. Aufgrund der Anzahl bekannter Sicherheitslücken resultiert dies in einer mehrere Hundert Megabytes umfassenden Datei. Das initiale Erzeugen kann, abhängig von verfügbarer Bandbreite und Rechenleistung, einige Minuten dauern. Nach erfolgreichem Abschluss befindet sich der Bericht unter „target/dependency-check-report.html“. Ein Beispiel für einen solchen Bericht findet man auf der Website des Projekts (DChck-Sample).

Automatisierung mit Jenkins

Um ohne weiteres Zutun über neu gefundene Sicherheitslücken informiert zu werden, bietet sich eine regelmäßig durchgeführte, automatische Überprüfung an. OWASP bietet für den CI-Server Jenkins ein Plug-in in an, das die Überprüfung durchführen und deren Ergebnisse auswerten kann. Unter anderem können die gefundenen Sicherheitslücken in Jenkins visualisiert, das Ergebnis eines Jobs von der Anzahl gefundener Sicherheitslücken abhängig gemacht und die Entwicklung der Anzahl gefundener Sicherheitslücken über die Builds in einem Diagramm dargestellt werden. Diese Auswertung wird in einer Post-Build-Action im Jenkins-Job konfiguriert. Die Überprüfung der Abhängigkeiten selbst kann durch einen durch das Jenkins-Plug-in bereitgestellten Build-Step oder per Maven durchgeführt werden.
Da die Überprüfung einige Zeit in Anspruch nimmt, sollte sie nicht bei jedem Commit/Push stattfinden. Ein Nightly-Build oder ein einmal pro Woche durchgeführter Build bieten sich daher an. Die Überprüfung per Maven Goal statt mit Jenkins-Build Step hat den Vorteil, dass die zentrale Konfiguration in der pom.xml erfolgt, wie in Listing 1 gezeigt.

<properties>
    <dependency-check-format>HTML</dependency-check-format>
</properties>
<build>
    <plugins>
        <plugin>
            <groupId>org.owasp</groupId>
            <artifactId>dependency-check-maven</artifactId>
            <version>1.4.0</version>
            <configuration>
                <format>${dependency-check-format}</format>
            </configuration>
        </plugin>
    </plugins>
</build>

Listing 1: Maven Konfiguration mit parameterisierbarem Format des Reports

Mit dieser Konfiguration müssen in Jenkins nur noch die folgenden Maven Goals mit Parameter aufgerufen werden:
mvn clean install org.owasp:dependency-check-maven:check -Ddependency-check-format=XML.
Dadurch wird eine Datei target/dependency-check-report.xml im Workspace generiert, die man in von einer entsprechenden Post-Build-Action verarbeiten lassen kann. Abschließend muss in dieser noch konfiguriert werden, dass der Job fehlschlägt oder instabil wird, sobald eine Sicherheitslücke entdeckt wird (siehe Abbildung). In Zusammenarbeit mit der von Jenkins angebotenen E-Mail-Benachrichtigung ist sichergestellt, dass man ohne weiteres Zutun per E-Mail über neu gefundene Sicherheitslücken informiert wird.

Die Praxis

Abschließend noch einige Details, die in der täglichen Arbeit mit dem Dependency-Check relevant sein können.

Es empfiehlt sich, den Dependency-Check auf dem Maven-Modul durchzuführen, das das eigentlich veröffentlichte Artefakt (.jar, .war, .ear etc.) herstellt. Dadurch werden auch nur die wirklich relevanten Abhängigkeiten auf Sicherheitslücken durchsucht und nicht die Abhängigkeiten von Modulen, die beispielsweise nur für Tests im Einsatz sind. Abhängigkeiten im Maven-Scope-Test werden in der aktuellen Version des Maven-Plug-ins standardmäßig nicht durchsucht.

Anders sieht es mit Abhängigkeiten in den Scopes provided und runtime aus. Insbesondere bei provided können sie unerwünscht sein. Wenn man beispielsweise durch die Kundenumgebung an ein bestimmtes Servlet-API gebunden ist, ist man gegen die gegebenfalls darin enthaltenen Sicherheitslücken machtlos. Insofern kann es durchaus sinnvoll sein, diesen Scope auszuschließen. Dies kann in der Konfiguration von Maven angepasst werden (siehe Listing 2).

Alternativ lassen sich bestimmte CVEs auch für einzelne Abhängigkeiten unterdrücken. Diese sind in einer speziellen Datei spezifiziert. Der XML-Code für das Unterdrücken lässt sich direkt aus dem im ersten Schritt generierten HTML-Bericht kopieren. Dies kann insbesondere sinnvoll sein, wenn es sich um False Positives handelt, also um gefundene Sicherheitslücken, die auf die Abhängigkeit gar nicht zutreffen. Ein Beispiel dafür ist der Generalverdacht (CVE-2016-3270) gegen alle Module des Jackson-Frameworks, obwohl nur in einem eine Sicherheitslücke auftritt. Es ist daher generell sinnvoll, jede gefundene Sicherheitslücke auf Korrektheit zu überprüfen. In jedem Fall sollte kommentiert werden, weshalb ein CVE unterdrückt wird, um zu späteren Zeitpunkten die Gründe für den Ausschluss nachvollziehen zu können. In den Listings 2 und 3 wird diese Möglichkeit für den Ausschluss aufgezeigt.

<configuration>
    <!-- Skip artifacts not bundled in distribution (provided scope) -->
    <skipProvidedScope>true</skipProvidedScope>
    <!-- Suppress false positives or dependencies that cannot be changed for specific reasons.-->
    <suppressionFile>suppress-cves.xml</suppressionFile>
</configuration>

Listing 2: Maven Konfiguration mit suppressionFile und provided Scope

<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd">
    <suppress>
        <!-- This Jackson-related issue seems to be related to module jackson-dataformat-xml, which is not used here. It is considered a false positive. See https://github.com/jeremylong/DependencyCheck/issues/517 In addition it was fixed in version 1.7.4., see https://github.com/FasterXML/jackson-dataformat-xml/issues/199 -->
        <notes><![CDATA[file name: jackson-core-2.8.1.jar]]></notes>
        <sha1>fd13b1c033741d48291315c6370f7d475a42dccf</sha1>
        <cve>CVE-2016-3720</cve>
    </suppress>
    <suppress>
        <!-- Same as other Jackson-related issue -->
        <notes><![CDATA[file name: jackson-annotations-2.8.0.jar]]></notes>
        <sha1>45b426f7796b741035581a176744d91090e2e6fb</sha1>
        <cpe>cpe:/a:fasterxml:jackson:2.8.0</cpe>
    </suppress>
</suppressions>

Listing 3: Beispielhaftes suppressionFile

Fazit

Der Artikel beschreibt wie man mit wenigen Schritten dauerhaft über bekannte Sicherheitslücken von Abhängigkeiten informiert bleibt. Dies sorgt nicht für absolute Sicherheit, entschärft aber mit geringem Aufwand einen der zehn gängigsten Makel. Insofern ist dies uneingeschränkt für alle Java-Projekte zu empfehlen.
Die hier beschriebene, auf Maven und Jenkins basierende Lösung stellt eine Möglichkeit für das Herausfinden von Sicherheitslücken dar. Aufgrund der vielen verfügbaren Implementierungen des OWASP-Dependency-Checks lassen sich vergleichbare Lösungen auch für viele andere Tools realisieren.
Ein komplett lauffähiges Beispiel, das die oben beschriebenen Fälle sowie eine Abhängigkeit mit nicht ausgeschlossener Sicherheitslücke enthält, steht bei GitHub.

Diesen Beitrag teilen

Johannes Schnatterer
Solution Architect
Mit besonderem Fokus auf Qualität, Open Source Enthusiasmus, einem Hauch von Pedantismus und der Pfadfinderregel im Gepäck versucht er, die IT-Welt jeden Tag ein winziges Bisschen besser zu machen.