CVE in Java-Dependencies beseitigen per OWASP Check
Janis Krasemann
4 Minuten
In der sich wandelnden Landschaft der Cybersicherheit ist Wachsamkeit nicht nur empfehlenswert, sondern unerlässlich. Für uns Entwickler:innen liegt eine oft übersehene Schwachstelle in veralteten Abhängigkeiten – eine tickende Zeitbombe, die unsere Projekte zum Scheitern bringen kann. Es gibt zwar Tools wie Renovate und Dependabot, die Projekte kontinuierlich über aktualisierte Abhängigkeiten informieren und Pull Requests für die Integration dieser Updates erstellen. Diese sind aber nicht für jeden Projektkontext verfügbar, und außerdem nicht besonders hilfreich, wenn es schnell gehen muss. In diesem Blog-Beitrag erzähle ich euch deshalb von meinen eigenen Erfahrungen, wie ich Sicherheitslücken in Maven-Dependencies mithilfe des OWASP Dependency Checkers und ein wenig Automatisierungsmagie geschlossen habe.
Das Überprüfen und Aktualisieren von Dependencies kann bei größeren Projekten sehr mühsam und repetitiv werden. Und was machen wir Entwickler, wenn etwas mühsam und repetitiv wird? Richtig, wir schreiben ein Script, welches uns die Arbeit abnimmt. Bonuspunkte, wenn das Schreiben des Skripts nicht länger dauert, als wenn man es komplett manuell gemacht hätte (das ist hier glücklicherweise der Fall). Damit konnte ich eine schnellere Feedbackschleife erzeugen, welche mir ohne viel Aufwand einiges an Zeit und Nerven erspart hat. Hier das Skript, welches die ganze Magie beinhaltet:
nodemon --watch pom.xml \
--exec "\
mvn install -DskipTests &&\
dependency-check --scan .
-o dependency-check-report.html &&\
open dependency-check-report.html &&\
mvn verify"
🐛 Der OWASP Dependency Checker
Zuerst benötigen wir dafür den OWASP Dependency Checker. Dieses Tool lädt im Wesentlichen die frei verfügbare Liste aller CVEs (CVE steht für Common Vulnerability and Exposure) herunter und gleicht dann offline die explizit installierten sowie die transitiven Abhängigkeiten des Projekts gegen diese Liste ab. Das ist mittlerweile das de-facto Standardwerkzeug, um installierte Dependencies auf Sicherheitslücken zu prüfen. Hiermit können unter anderem npm, .NET, Ruby und eben Maven Projekte analysiert werden.
Zeile 4 aus dem Skript führt den eigentlichen Dependency Check aus und schreibt die Ergebnisse in eine HTML-Datei für die spätere Analyse.
♻️ Der Workflow
Mit dem Skript ergibt sich folgender Workflow, sobald es einmal durchgelaufen ist:
- OWASP Report prüfen
pom.xml
mit zu aktualisierender Dependency modifizieren- Dependency wird in neuer Version installiert
- Dependency-Checker läuft
- Tests laufen und schlagen fehl, wenn was kaputtgegangen ist
- zurück zum Anfang ⤴️
🪄 Die Magie
Nun folgt der spannende Teil:
Ich verwende beim o.g. Skript zunächst das npm-Tool nodemon, um auf Änderungen an der Datei pom.xml
zu horchen.
Sobald die pom.xml
verändert wird (z.B. weil die Versionsnummer einer veralteten Bibliothek hochgesetzt wurde), führt nodemon das über den Parameter exec
angegebene Skript erneut aus.
Ich benutze nodemon, weil ich hauptsächlich in der JavaScript-Welt unterwegs bin.
Es gibt auch einige andere Tools aus anderen Ökosystemen, die das Gleiche tun, wie zum Beispiel inotify oder entr (Linux), sowie when-changed (plattformunabhängig via Python).
Im Skript werden als Erstes die Maven Dependencies neu installiert, damit der OWASP Checker anschließend die aktualisierten Bibliotheken erneut prüfen kann.
Dabei benutze ich -DskipTests
, weil ich schnell Feedback haben will, ob die Schwachstelle geschlossen ist und deshalb die Tests überspringen möchte.
Der Dependency-Check selbst ist relativ einfach gehalten: Er prüft das aktuelle Verzeichnis (--scan .
) und schreibt anschließend die Ergebnisse in die Datei dependency-check-report.html.
Damit ich den Report sofort prüfen kann, lasse ich ihn via open dependency-check-report.html
sofort automatisch im Browser öffnen.
Achtung, open
ist ein MacOS-spezifisches Kommando; unter Linux muss xdg-open
o.ä. verwendet werden.
Nun muss man sich für ein Vorgehen beim eigentlichen Aktualisieren der Bibliotheken entscheiden. Der vorsichtige Ansatz wäre, jede Version einzeln hochzuziehen; bei unkritischen Updates und/oder großen Projekten dauert das aber ggf. länger als nötig. Wenn viele Updates anstehen, kann man auch erstmal pauschal die Hälfte der Versionen erhöhen, um dann (analog zur Binärsuche / git-bisect) weiter aufzuteilen.
Je nachdem, welche Bibliothek auf welche Version hochgesetzt wurde, können bei solchen Versionsänderungen dann natürlich Probleme auftreten.
Idealerweise sind Breaking Changes immer in den Versionshinweisen hinterlegt und/oder es wird Semantic Versioning verwendet.
Ein guter erster Anlaufpunkt für Informationen zu den Bibliotheken ist MVN Repository.
Aber Vorsicht ist ja bekanntlich besser als Nachsicht und deshalb wird hinterher immer nochmal mvn verify
ausgeführt, um die Tests durchlaufen zu lassen.
Wenn hier ein Fehler auftritt, meldet nodemon das entsprechend und horcht dann weiterhin auf Änderungen an der pom.xml
, um es nochmal zu versuchen.
In solchen Fällen ist dann zu entscheiden, ob man es lieber mit einer anderen Version der soeben hochgezogenen Bibliothek versucht, oder ob man etwas an der Implementierung oder den Tests verändern muss.
Das Ganze steht und fällt natürlich mit guten Tests und einer vernünftigen Testabdeckung. Falls die Tests nicht in der Lage sind, wahrscheinlich zu erkennen, ob die Software durch das Update kaputtgegangen ist, kann man sich den Schritt besser sparen und muss stattdessen manuell testen.
🏁 Fazit
Durch die automatische Ausführung von Installation, OWASP Dependency-Check und Tests konnte ich mir beim Aktualisieren der Abhängigkeiten einiger unserer Projekte viel Zeit ersparen. Durch das schnelle Feedback nach jeder Versionsänderung habe ich sofort gemerkt, welche Bibliothek in welcher Version Probleme macht. Das gezeigte Skript kann leicht für eure eigenen Zwecke abgewandelt werden, z.B. für npm Projekte oder wenn einige Dateien beim Dependency-Check ignoriert werden sollen.
Diesen Artikel teilen über: