
Der OWASP Dependency-Check ist ein Open-Source-Werkzeug der OWASP Foundation zur Software Composition Analysis (SCA). Das Tool scannt Dependencies auf bekannte Sicherheitslücken und generiert dazu einen Bericht. Regelmäßig ausgeführt kann es dabei unterstützen, bekannte Schwachstellen in verwendeten Bibliotheken zeitnah zu entdecken und durch Maßnahmen wie Versionsupdates zu kompensieren.
Zur besseren Einordnung des OWASP Dependency-Checks beschreibe ich im folgenden Abschnitt, was es mit OWASP und den OWASP Top 10 auf sich hat.
OWASP
Das Open Worldwide Application Security Project ist eine Online-Community, die sich mit Sicherheit im Internet befasst. Geleitet wird sie von der nicht gewinnorientierten OWASP Foundation. OWASP veröffentlicht, zuletzt alle vier Jahre, die OWASP Top 10. Dabei handelt es sich um die aus Sicht des Projektes zehn größten Sicherheitsrisiken für Systeme im Internet. Die OWASP Top 10 basieren auf Daten, die dem Projekt von verschiedenen Personen und Organisationen zur Verfügung gestellt werden.
OWASP Top 10 (2021)
Bei den (zum Zeitpunkt dieses Artikels aktuellen) OWASP Top 10 aus dem Jahr 2021 handelt es sich um die folgenden Sicherheitsrisiken:
A01:2021-Broken Access Control umfasst verschiedene Risiken, die etwa durch Nicht-Einhalten von Praktiken wie dem Principle of Least Privilege oder Deny by Default entstehen können. Darunter fallen verschiedene Ansätze zur Umgehung der Zugriffskontrolle, etwa die Manipulation einer URL oder eines JWT, um auf Daten eines anderen Benutzers zuzugreifen oder die eigenen Rechte zu erweitern.
A02:2021-Cryptographic Failures bezeichnet die unerwünschte Preisgabe sensibler Daten, etwa dadurch, dass sie im Klartext übertragen werden, oder veraltete Verschlüsselungsverfahren wie MD5 zum Einsatz kommen.
A03:2021-Injection beinhaltet Angriffsvektoren wie Cross-site Scripting und SQL-Injection. Oftmals wird dabei nicht validierter Inhalt an einen Interpreter weitergereicht, was die Preisgabe sensibler Informationen oder Remote Code Execution zur Folge haben kann.
A04:2021-Insecure Design beschreibt die Abwesenheit von Maßnahmen für ein sicheres Design, etwa die Entwicklung von Security Controls und Threat Modelling.
A05:2021-Security Misconfiguration umfasst sicherheitsrelevante Konfigurationsfehler. Dazu gehören unsichere Standardeinstellungen, die nicht geändert werden, nicht verwendete aber dennoch aktivierte Features oder falsche bzw. unzureichende sicherheitsrelevante Konfigurationen, wie etwa Services, die bei einem internen Fehler den Stack Trace preisgeben.
A06:2021-Vulnerable and Outdated Components bezeichnen installierte Betriebssysteme, Software und Bibliotheken, die veraltet sind oder bekannte Sicherheitslücken enthalten. Dieses Sicherheitsrisiko wird vom OWASP Dependency-Check adressiert. Das erste Mal tauchte dieses Sicherheitsrisiko in den OWASP Top 10 2013 als A9-Using Components with Known Vulnerabilities auf.
A07:2021-Identification and Authentication Failures beinhaltet Fehler, die Unbefugten eine erfolgreiche Authentisierung ermöglichen. Zu den Angriffsvektoren zählen Brute-Force-Attacken und Credential Stuffing. Einfallstore können beispielsweise schwache Passwortrichtlinien und schlecht umgesetzte Passwort-vergessen-Funktionen sein.
A08:2021-Software and Data Integrity Failures bezeichnen Angriffsmöglichkeiten durch fehlende Integritätsprüfungen (z.B. digitale Signaturen) von Software und Daten, etwa ungeprüfte automatische Updates oder Downloads aus dem Internet in der Build-Pipeline (z.B. von DockerHub, Maven, PyPi oder NPM) oder auch bei der Serialisierung von Daten und Objekten.
A09:2021-Security Logging and Monitoring Failures helfen Angreifern, länger unentdeckt zu bleiben. Dieses Risiko beinhaltet fehlende, fehlerhafte oder unzureichende Auditierung, Logging, Metriken und andersweitiges Sichtbarmachen von verdächtigen Aktivitäten und potenziellen Angriffen.
A10:2021-Server-Side Request Forgery ist ein neues Risiko in den OWASP Top 10 und bezeichnet die Manipulation von Anfragen, um serverseitig nicht gewünschte Verbindungen bzw. Abfragen zu ermöglichen, etwa interne Port-Scans oder den Zugriff auf lokale bzw. nicht-öffentliche Dateien und Services. Dieses Risiko lässt sich durch Praktiken wie Deny by Default oder Zero-Trust-Architekturen adressieren und gewinnt durch die zunehmende Nutzung der Public Cloud immer weiter an Relevanz.
Dependencies mit dem OWASP Dependency-Check scannen
Ich verwende für den Rest des Beitrags die nicht-offizielle Abkürzung ODC für den OWASP Dependency-Check. Der ODC kann als Kommandozeilenwerkzeug oder Plugin eingesetzt werden.
Beim Scannen versucht der ODC, die CPE (Common Platform Enumeration) der Bibliotheken zu bestimmen. Bei CPE handelt es sich um ein strukturiertes Namensschema ähnlich URIs (Uniform Resource Identifiers), die eine Bibliothek eindeutig beschreiben. ODC verwendet die NVD (National Vulnerability Database) des NIST (National Institute of Standards and Technology), in der CVE (Common Vulnerabilities and Exposures) erfasst und mit CPEs verknüpft sind.
Dadurch ist der ODC in der Lage, Auskunft über die bekannten CVE in den verwendeten Bibliotheken zu geben. Dies geschieht in Form eines Berichtes, den der ODC bei jedem Lauf erzeugt.
dependency-check -s ~/.gradle/caches/modules-2/files-2.1
Ein Scan meines Gradle-Caches offenbart: Da schlummern ganz schön viele Dependencies mit bekannten CVE rum.

Integration und Analyzer
Der ODC steht zum Zeitpunkt dieses Beitrages sowohl als Standalone-Kommandozeilenprogramm als auch als Plugin für Ant, Gradle, Jenkins, Maven und SBT (aus dem Scala-Universum) zur Verfügung. Der Fokus liegt klar auf Java und auch der ODC selbst ist in Java geschrieben, es werden aber weitere Sprachen bzw. Ökosysteme unterstützt, darunter C#/.NET, JavaScript und Ruby. Für Dart, Go, Perl, PHP, Python und Swift stehen experimentelle Analyzer zur Verfügung, die mehr False-Positives und False-Negatives erzeugen, aber laut OWASP von diversen Teams als brauchbar bezeichnet und eingesetzt werden.
Konfiguration
Der ODC kann umfangreich konfiguriert werden. Für Pipeline-Integration besteht die Möglichkeit, bei gefundenen CVE die Ausführung mit einem Fehlerstatus (via Exit Code) zu beenden. CVE werden nach dem CVSS (Common Vulnerability Scoring System) bewertet und erhalten dabei einen Score zwischen 1 und 10. Der ODC ermöglicht, nur CVE ab einem bestimmten Score im Bericht auszuweisen bzw. nur dann einen Fehlerstatus zurückzugeben. Zur automatischen Weiterverarbeitung kann der Bericht neben HTML auch in anderen Formaten wie CSV und JSON geschrieben werden.
CVE können unterdrückt werden, etwa wenn es für betroffene Bibliotheken noch keinen Fix gibt und die Entscheidung fällt, sie mangels Alternativen weiterhin zu verwenden. Dieses Unterdrücken kann zeitlich begrenzt werden, so dass der ODC ab einem bestimmten Datum wieder fehlschlägt, wenn die CVE dann immer noch in den verwendeten Abhängigkeiten ist. Die Unterdrücken-Funktion kann auch für False-Positives genutzt werden. Viele False-Positives wurden übrigens in der Vergangenheit durch ein Update des ODC behoben, es lohnt sich also, auch hier immer eine möglichst aktuelle Version zu verwenden.
Bei Bedarf kann eine gespiegelte NVD verwendet werden, es ist auch möglich diese mit Basic-Auth abzusichern und nur CVE ab einem bestimmten Jahr zu verarbeiten (die in der NVD eingetragenen CVE gehen bis in das Jahr 2002 zurück).
Beim ersten Lauf nach längerer Zeit braucht der ODC etwas länger, da die gesamte Datenbank heruntergeladen und eine lokale Datenbank aufgebaut ist. Fortgeschrittene Anwender haben die Möglichkeit, diese Datenbank zentralisiert zu verwalten.
Erfahrungen und Empfehlungen
In diesen Abschnitt schildere ich meine eigenen Erfahrungen mit dem ODC.
Dependency-Check als Prozess etablieren
Der ODC bringt nur dann einen sicheren Mehrwert, wenn er automatisiert ausgeführt wird und das Ergebnis Beachtung findet. Meldet der ODC Verletzungen, so sollte sich jemand mit diesen zeitnah auseinandersetzen. Damit das zuverlässig funktionert, sollte das genaue Vorgehen im Team festgelegt werden.
CVE verstehen und Lösungsmöglichkeiten erarbeiten
Im einfachsten Fall kann eine CVE durch ein Update der Bibliothek behoben werden. Ist dies nicht der Fall, muss man sich Alternativen erarbeiten. CVE können transitiv sein: Eine Verletzung in Spring Boot kann darauf zurückzuführen sein, dass die CVE im eingebundenen Tomcat-Webserver liegt. Spring Boot mag bereits in der aktuellsten Version eingebunden sein, diese verwendet aber vielleicht trotzdem nicht die neueste Tomcat-Version, die von der CVE nicht mehr betroffen ist. Die Lösung liegt hier darin, über die Dependency-Version-Properties die gewünschte Tomcat-Version explizit einzubinden. Spring Boot ermöglicht dies für diverse Dependencies und ich habe schon mehrfach, z.B. für SnakeYAML, davon Gebrauch gemacht. Wichtig ist, die explizite Dependency später auch wieder zu entfernen, damit die verwendete Tomcat-Version nicht irgendwann selbst veraltet ist. Am besten versieht man die Zeile mit einem TODO und einer Erklärung, oder legt sich ein Ticket an, um den Eintrag später wieder zu entfernen.
Gibt es kein passendes Update bzw. eine passende Konfiguration, so bleibt nichts anderes übrig als die CVE besser zu verstehen. Handelt es sich vielleicht um ein False-Positive? Dann kann diese einfach unterdrückt werden, am besten mit einem sprechenden Kommentar. Betrifft die CVE mich vielleicht gar nicht? Frameworks wie Spring können sehr umfangreich sein, wenn die CVE nur für eine eh nicht genutzte Komponente gilt, würde ich sie im Normalfall für ein paar Wochen unterdrücken und damit leben, und hoffen, dass dann ein Update zur Verfügung steht, in dem die Verletzung behoben ist. Bei Open-Source-Projekten kann es sich auch lohnen, z.B. über einen Issue auf GitHub selbst auf die CVE hinzuweisen. Darüber kann man die Behebung beschleunigen und erfährt unter Umständen, mit welcher Priorität die Verletzung behoben wird, ob es sich nicht doch um ein False-Positive handelt oder ob wie sich die Lücke vorübergehend überbrücken lässt.
Eine weitere Möglichkeit ist die Härtung des Systems. Existiert eine CVE, die im eigenen Projekt tatsächlich ausgenutzt werden könnte, so kann man die Software so anpassen oder Funktionalität vorübergehend einschränken, so dass dies nicht mehr möglich ist. Auch eine Härtung auf Netzwerk- oder Betriebssystemebene ist vorstellbar, je nachdem von wem das System wie verwendet wird.
Helfen alle diese Ansätze nicht, bleibt nur noch, auf eine andere Bibliothek umzusteigen oder das System komplett herunterzufahren, was nur in den wenigsten Fällen oder bei dauerhaften Problemen mit einer Bibliothek eine Option sein dürfte.
Dependency-Check nicht in den Build-Prozess integrieren
Der ODC steht im Widerspruch zu reproduzierbaren Builds. Tests und statische Code-Analyse sollten, wenn gut geschrieben, für den gleichen Code immer zum gleichen Ergebnis kommen. Der ODC hat eine zeitliche Komponente, da er immer mit dem zum jeweiligen Zeitpunkt neuesten Daten der NVD arbeitet. Damit kann ein Build auf Basis eines Git-Tags, der zunächst erfolgreich durchläuft, zwei Wochen später fehlschlagen, da der ODC nun eine CVE entdeckt, die zwei Wochen vorher noch nicht erfasst war.
Auch wenn es eine Woche keine Code-Änderungen gibt, kann der ODC wegen dieser zeitlichen Komponente plötzlich anschlagen. Er sollte daher regelmäßig laufen, je nach Kritikalität und Exponiertheit des Systems. Es spricht nichts dagegen, den ODC auch beim Merge oder Push auf den Main-Branch laufen zu lassen, allerdings sollte eine CVE-Meldung durch den ODC nicht einem Merge-Stopp gleichkommen, da sonst eine neu erfasste CVE die Entwicklungsarbeit behindert. Werden durch ein Feature neue Dependencies eingebunden, gehört es für mich aber zum guten Ton, diese via ODC auf Verletzungen zu überprüfen, bevor das Feature in den Hauptzweig eingecheckt wird.
Dependency-Check ersetzt nicht das Patch-Management
Bei all den Dependencies heutzutage ist es gar nicht so leicht, den Überblick zu behalten. Gradle/Maven Dependencies und Plugins, NPM, AWS-CDK, Lambda-Laufzeitumgebungen, AWS-Serviceversionen, Terraform-Provider und Datenbankversionen, Testcontainers-Docker-Images… So sieht meine persönliche Dependency-Landschaft zurzeit aus. Der ODC ist kein geeignetes Mittel, alle diese aktuell zu halten, schon deshalb, da der Zweck ein anderer ist. Der ODC wird nicht anschlagen, wenn die eingebundene Version nicht auf dem neuesten Stand ist, auch nicht wenn sie bereits ein Jahr veraltet ist, sondern nur, wenn es bekannte CVE in dieser gibt. Der ODC soll nicht das Patchmanagement ersetzen, sondern nur bei potenziell akuten Sicherheitsproblemen ein weiterer Trigger für den Maintainer sein, diese zu lösen. Der Patchday ist für mich trotz ODC unersetzlich.
- Dependency-Check auf owasp.org - History of OWASP Top 10 - Spring Dependency Version Properties