OWASP Dependency Check

OWASP Dependency Check to plugin do Gradle’a pozwalający zautomatyzować sprawdzanie wykorzystywanych bibliotek pod kątem luk bezpieczeństwa i dobra darmowa alternatywa dla Snyka.

Snyk jest popularnym rozwiązaniem i ma swoje zalety, jak choćby przechowywanie danych o używanych przez nas zależnościach na serwerach firmy Snyk, przez co narzędzie może zgłaszać nowe zagrożenia nawet, gdy wstrzymaliśmy development i nie odpalamy buildów naszego projektu, a ten cały czas działa na produkcji. Spotkałem się jednak z tyloma przypadkami nieumiejętności Snyka współdziałania zarówno z Mavenem jak i Gradle’em, że mam dużą rezerwę do tego narzędzia. Istotną wadą może być opłata licencyjna — jeśli nie rozwijamy oprogramowania open source, trzeba za Snyka płacić.

OWASP Dependency Check jest darmowy dla każdego użycia i działa wyłącznie lokalnie. Nie ma serwerów, na które trafiają nasze dane. Musimy sami zadbać o to, żeby uruchamiać na serwerze CI taska z tym narzędziem nawet gdy dłuższy czas nie ma commitów do repo — ale nie jest to rocket science. Współpracuje bardzo dobrze z Gradle’em.

Weźmy przykładowy projekt z proponowanym przeze mnie sposobem integracji z pluginem:

plugins {
    id 'java-library'
    id "org.owasp.dependencycheck" version "5.2.4"
}

repositories {
    jcenter()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:2.1.0.RELEASE'

    testImplementation 'org.springframework.boot:spring-boot-starter-test:2.1.0.RELEASE'
}

dependencyCheck {
    failBuildOnCVSS = 9
//    suppressionFile = file('config/dependency-check/suppressions.xml')
    scanConfigurations = ['runtimeClasspath']
}
check.dependsOn dependencyCheckAnalyze
dependencyCheckAnalyze {
    inputs.files configurations.runtimeClasspath
    outputs.upToDateWhen { true }
    shouldRunAfter test
}

Pierwsze uruchomienie trwa dość długo (ponad 2 minuty), ponieważ plugin pobiera bazę podatności; normalne uruchomienie zabiera 2–3 sekundy. Gdy mamy dziury bezpieczeństwa, zobaczymy komunikat w konsoli:

% ./gradlew build
   Task :dependencyCheckAnalyze FAILED
   Verifying dependencies for project owasp-demo
   Checking for updates and analyzing dependencies for vulnerabilities
   Generating report for project owasp-demo
   Found 23 vulnerabilities in project owasp-demo 
 One or more dependencies were identified with known vulnerabilities in owasp-demo:
 jackson-databind-2.9.7.jar (pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.9.7, cpe:2.3:a:fasterxml:jackson:2.9.7:::::::, cpe:2.3:a:fasterxml:jackson-databind:2.9.7:::::::) : CVE-2018-1000873, CVE-2018-19360, CVE-2018-19361, CVE-2018-19362, CVE-2019-12086, CVE-2019-12384, CVE-2019-12814, CVE-2019-14379, CVE-2019-14439, CVE-2019-14540, CVE-2019-16335, CVE-2019-16942, CVE-2019-16943, CVE-2019-17267, CVE-2019-17531
 tomcat-embed-websocket-9.0.12.jar (pkg:maven/org.apache.tomcat.embed/tomcat-embed-websocket@9.0.12, cpe:2.3:a:apache:tomcat:9.0.12:::::::, cpe:2.3:a:apache_software_foundation:tomcat:9.0.12:::::::, cpe:2.3:a:apache_tomcat:apache_tomcat:9.0.12:::::::) : CVE-2019-0199, CVE-2019-0221, CVE-2019-0232, CVE-2019-10072 tomcat-embed-core-9.0.12.jar (pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.12, cpe:2.3:a:apache:tomcat:9.0.12:::::::, cpe:2.3:a:apache_software_foundation:tomcat:9.0.12:::::::, cpe:2.3:a:apache_tomcat:apache_tomcat:9.0.12:::::::) : CVE-2019-0199, CVE-2019-0221, CVE-2019-0232, CVE-2019-10072
 See the dependency-check report for more details.
 FAILURE: Build failed with an exception.
 What went wrong:
 Execution failed for task ':dependencyCheckAnalyze'. 
    
 Dependency-Analyze Failure:
   One or more dependencies were identified with vulnerabilities that have a CVSS score greater than '9.0': CVE-2019-14379, CVE-2019-16942, CVE-2019-16943, CVE-2018-19362, CVE-2018-19361, CVE-2018-19360, CVE-2019-17267, CVE-2019-17531, CVE-2019-16335, CVE-2019-14540, CVE-2019-0232
   See the dependency-check report for more details.

Możemy też przejrzeć szczegóły w HTML-owym raporcie w build/reports/dependency-check-report.html

Trochę wyjaśnienia, co dzieje się w konfiguracji:

  • failBuildOnCVSS = 9 domyślnie build nigdy nie jest przerywany; tu ustawiamy, by był przerywany przy krytycznych zagrożeniach (9 na skali od 0 do 10)
  • suppressionFile konfigurujemy lokalizację pliku z listą zagrożeń, którymi nie chcemy się przejmować
  • scanConfigurations= ['runtimeClasspath'] domyślnie skanowane są wszystkie konfiguracje z zależnościami; różne pluginy Gradle’a definiują własne konfiguracje na wewnętrzne potrzeby i tylko na czas buildu — zazwyczaj nie ma sensu przejmować się podatnościami w bibliotekach, które nie są elementem produktu działającego na środowisku produkcyjnym
  • shouldRunAfter test domyślnie task ze skanowaniem zależności odpali się przed testami, tymczasem okresowo ponawiane pobieranie bazy podatności może potrwać bardzo długo; zmodyfikowana tak kolejność jest lepsza dla uzyskania szybkiej pętli zwrotnej przy buildzie
  • check.dependsOn dependencyCheckAnalyze będziemy skanować zagrożenia przy każdym buildzie
  • inputs outputs task z pluginu nie jest napisany tak, żeby był inkrementalny; według mnie nie ma co czekać 2 sekundy przy każdym buildzie, nawet gdy nic się nie zmieniło — ten kod wprowadza mniej więcej poprawną inkrementalność

Plik suppressions.xml ma tak skomplikowany format, że praktycznie nie da się dodawać do niego nowych wpisów ręcznie. Nie jest to jednak przeszkoda: otwierając na dysku raport HTML-owy przy każdym zagrożeniu mamy przycisk „suppress”, który generuje odpowiedni XML do skopiowania.