~/blog/2016/09/owasp-dependency-check-bekannte-lu.md

OWASP Dependency-Check: Bekannte Lücken in Bibliotheken finden

6 min
Titelbild: OWASP Dependency-Check: Bekannte Lücken in Bibliotheken finden

Keine Zeit? Hier die Kurzversion in 30 Sekunden.

  • Die meisten Anwendungen bestehen zum großen Teil aus fremdem Code. Eine bekannte Lücke in einer Bibliothek ist deshalb oft die bequemste Tür für Angreifer; die OWASP Top 10 führen das als eigenen Punkt (A9).
  • Dependency-Check gleicht die Bibliotheken eines Projekts mit der National Vulnerability Database ab und erzeugt einen Report mit CVE-Nummern und Schweregraden.
  • Als Maven-Plugin (Ziel check) und in Jenkins lässt es sich in den Build einhängen; mit failBuildOnCVSS bricht der Build ab, wenn ein Schwellwert überschritten wird.
  • Der Abgleich läuft über Namen und Versionen (CPE) und liefert deshalb auch Fehlalarme. Eine Suppression-Datei hilft, kostet aber Pflege.
  • Das Werkzeug zeigt Probleme, behebt sie aber nicht. Ohne die Bereitschaft zu aktualisieren bleibt es ein Report, den niemand liest.

Im November 2015 ging ein Bericht über eine Schwachstelle in der Deserialisierung von Java-Objekten durch die Runde. Der eigentliche Auslöser war unscheinbar: Die Apache Commons Collections, eine Bibliothek, die in unzähligen Projekten irgendwo im Klassenpfad liegt, enthielt Klassen, die sich beim Deserialisieren zu einer Kette zusammenbauen ließen und am Ende beliebigen Code ausführten. Betroffen waren dadurch Anwendungsserver und Werkzeuge, die mit der Bibliothek selbst gar nichts zu tun haben wollten. Mich hat daran weniger die Technik beschäftigt als eine einfache Frage: Wüsste ich für meine eigenen Projekte, ob sie diese Bibliothek enthalten, und in welcher Version?

Die ehrliche Antwort war damals: nicht ohne Suchen. Seitdem schaue ich anders auf das, was in einem lib-Ordner oder in einer Abhängigkeitsliste steht.

Das Problem hat einen Namen

Wer eine Anwendung baut, schreibt davon nur einen Teil selbst. Frameworks, Logging, XML-Parser, Bibliotheken für Dateiformate, am Ende kommen ein paar Dutzend Archive zusammen, und dazu gehören noch die Abhängigkeiten der Abhängigkeiten. Gegen Lücken im eigenen Code hilft Sorgfalt. Gegen Lücken im fremden Code hilft nur, überhaupt zu wissen, was man eingebaut hat.

Die OWASP hat dafür in ihrer Top-10-Liste von 2013 einen eigenen Punkt vorgesehen, A9, „Using Components with Known Vulnerabilities“. Das Wort „bekannt“ ist der springende Punkt. Es geht nicht um raffinierte Angriffe auf unbekannte Schwächen, sondern darum, dass es für eine Lücke längst eine Meldung, eine Nummer und oft auch einen Patch gibt, und die Anwendung trotzdem unverändert weiterläuft. Die Struts-2-Lücken der letzten Jahre, bei denen über präparierte Anfragen Code auf dem Server ausgeführt werden konnte, sind dafür ein gutes Beispiel. Der Fehler war öffentlich dokumentiert, und trotzdem liefen Installationen lange mit der alten Version weiter, weil niemand wusste oder prüfte, welche Version dort gerade im Einsatz war.

Wie der Abgleich funktioniert

Das OWASP-Projekt Dependency-Check geht das Problem pragmatisch an. Es durchsucht Archive und Abhängigkeitsangaben eines Projekts, sammelt, was es über jede Bibliothek herausfinden kann (Dateiname, Hersteller, Version, Angaben aus dem Manifest oder der POM), und bildet daraus sogenannte CPE-Einträge, eine Art standardisierte Bezeichnung für „Produkt X in Version Y“. Diese Einträge werden mit der National Vulnerability Database abgeglichen, in der die bekannten Schwachstellen als CVE-Einträge samt Bewertung nach CVSS geführt werden. Wer einen Treffer hat, sieht die CVE-Nummer, den Schweregrad und einen Link zur Beschreibung.

Die NVD-Daten lädt das Werkzeug beim ersten Lauf herunter und hält sie danach in einer lokalen Datenbank aktuell. Der erste Lauf dauert deshalb spürbar länger als alle folgenden. Das gehört eingeplant, vor allem auf Build-Servern, die ohne Internetzugang arbeiten oder bei denen jeder Build in einer frischen Umgebung startet.

Java steht klar im Mittelpunkt. Für .NET und für Skriptsprachen gibt es Ansätze, die mir bisher eher experimentell vorkommen. Wer in einem Projekt mit C# oder PHP arbeitet, sollte also nicht erwarten, dass derselbe Komfort schon überall da ist.

Einbau in Maven und Jenkins

Für Maven gibt es ein Plugin, dependency-check-maven. Der Einbau ist unspektakulär:

<plugin>
  <groupId>org.owasp</groupId>
  <artifactId>dependency-check-maven</artifactId>
  <version>1.4.3</version>
  <configuration>
    <failBuildOnCVSS>7</failBuildOnCVSS>
    <suppressionFile>dependency-check-suppressions.xml</suppressionFile>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>check</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Das Ziel check läuft dann im Rahmen des Builds (standardmäßig in der Phase verify) und schreibt einen Report ins target-Verzeichnis. Wer es nur gelegentlich haben will, kann es auch von Hand aufrufen, ohne die POM anzufassen. Für Ant, Gradle und die Kommandozeile existieren ähnliche Lösungen.

In Jenkins gibt es ein eigenes Plugin, das die Ergebnisse einsammelt, anzeigt und über mehrere Builds hinweg als Verlauf darstellt. Das ist nützlicher, als es klingt. Ein einzelner Report ist eine Momentaufnahme, ein Verlauf zeigt, ob es mit der Zeit besser oder schlechter wird.

Report und Schwellwert

Der Report listet pro Bibliothek die gefundenen Schwachstellen mit CVE, Schweregrad und der Begründung, warum das Werkzeug glaubt, die Bibliothek gehöre zu einem bestimmten Produkt. Letzteres ist wichtig, wie ich gleich noch zeige.

Mit failBuildOnCVSS lässt sich festlegen, ab welchem Wert der Build fehlschlägt. CVSS reicht von 0 bis 10, und je höher der Wert, desto dringlicher. Ich finde einen Schwellwert um 7 als Anfang vernünftig: Er trifft die ernsten Fälle, ohne ein bestehendes Projekt sofort komplett lahmzulegen. Wer gleich bei jedem Treffer abbricht, wird nach wenigen Tagen wieder abgeschaltet, weil niemand mehr zum Arbeiten kommt. Besser ist es, mit einem hohen Wert anzufangen und ihn nach und nach zu senken, wenn die alten Funde abgearbeitet sind.

False Positives und die Suppression-Datei

Die Zuordnung über CPE ist die Schwachstelle des Verfahrens. Das Werkzeug muss aus einem Dateinamen und ein paar Metadaten schließen, um welches Produkt es sich handelt, und das gelingt nicht immer. Mal wird eine Bibliothek einem ähnlich benannten, aber anderen Produkt zugeordnet, mal wird eine Lücke gemeldet, die im konkreten Einsatz gar nicht erreichbar ist, mal fehlt etwas, weil die Bibliothek in einem größeren Archiv eingebettet ist. Die Folge sind Fehlalarme und gelegentlich auch Funde, die verpasst werden.

Für Fehlalarme gibt es die Suppression-Datei, eine XML-Datei, in der man einzelne Treffer gezielt ausblendet, etwa für ein bestimmtes Archiv und eine bestimmte CVE. Das ist legitim, hat aber einen Haken: Jede Unterdrückung ist eine Behauptung, und Behauptungen veralten. Ich notiere zu jedem Eintrag, warum er da steht, und schaue gelegentlich nach, ob die Begründung noch gilt. Sonst wird die Datei mit der Zeit zu einem Ablageort für alles, was lästig war.

Was es nicht ersetzt

Dependency-Check findet nur, was jemand gemeldet und katalogisiert hat. Eine saubere Auswertung heißt nicht, dass die Bibliotheken sicher sind, sondern dass keine bekannte Lücke gefunden wurde. Und ein roter Build behebt gar nichts. Er verlangt eine Entscheidung: aktualisieren, die Funktion umgehen, das Risiko bewusst tragen und dokumentieren.

Das Aktualisieren ist in der Praxis der schwierigere Teil. Wer Abhängigkeiten über Jahre einfriert, merkt beim Sprung auf die neue Version, dass sich Schnittstellen geändert haben und Tests brechen. Kleine, regelmäßige Updates sind deutlich billiger als der eine große Sprung unter Zeitdruck, und das Werkzeug hilft vor allem dabei, aus dem Thema eine Routine zu machen statt eines Notfalls.

Ich habe bei mir aus dem Fall von 2015 die Konsequenz gezogen, die Abhängigkeitsliste nicht mehr als Nebensache zu betrachten. Wer nicht weiß, was er mitliefert, kann es auch nicht pflegen.

Beitragsbild: eigene Collage mit Logos von Wikimedia Commons (OWASP-Logo: OWASP, CC BY-SA 4.0; Apache-Maven-Logo: Apache Software Foundation, gemeinfrei); die Marken gehören ihren Inhabern. Die Collage steht ebenfalls unter CC BY-SA 4.0.

$ whoami
Christian Ackermann

Softwareentwickler (C++, C, Java, C#) aus der Oberpfalz, bloggt seit 2001 über Linux, Technik und Kochen.