In questo articolo introdurrò il concetto di Software Composition Analysis (SCA), spiegando come applicarlo all’interno del ciclo di vita del software, utilizzando in questo caso Sonatype Nexus IQ Lifecycle come tool di dependency scanning, e mostrando possibili esempi di integrazione.
Vulnerabilità su software open-source
È un dato di fatto che l’80-90% delle applicazioni moderne è composta da componenti, la maggior parte dei quali è Open Source Software (OSS).
Tali componenti potrebbero in alcuni casi presentare vulnerabilità di sicurezza che, se identificate dalla comunità open-source, vengono rese note al pubblico in modo che possano essere risolte nei successivi rilasci.
Un esempio noto a tutti è Log4J, che nel dicembre 2021 ha creato il panico nel mondo dell’IT.
Gli hacker, per comodità, generalmente preferiscono sfruttare le vulnerabilità note (Common Vulnerability and Exposures – CVEs) presenti nelle componenti OSS piuttosto che ricercare le eventuali vulnerabilità introdotte nell’applicazione da uno sviluppatore poco attento.
Si tratta di vulnerabilità che in base alla loro natura possono essere impiegate per effettuare attacchi hacker di vario tipo (sql injection, cross-site scripting, ecc.).
Un uso sicuro dell’open-source
Come possiamo utilizzare ed integrare le componenti OSS nelle nostre applicazioni, senza avere impatti negativi sulla sicurezza?
Come possiamo approfittare del valore accumulato dell’open-source senza esporci a pericoli?
La gestione delle componenti di progetto tramite tool di dependency scan ci fornisce le risposte a queste due domande. Questi ultimi permettono di analizzare le dipendenze di progetto di un’applicazione, alla ricerca di vulnerabilità di sicurezza note.
Monitorare in maniera continuativa le dipendenze di progetto utilizzate nel parco applicativo di un’organizzazione, al fine di verificare se le versioni delle componenti attualmente utilizzate presentano o meno vulnerabilità di sicurezza, è il pane e burro di qualsiasi processo di gestione del rischio dell’open-source.
In genere, le componenti vulnerabili identificate vengono classificate in base al fattore di rischio che rappresentano (basso, medio, alto, altissimo).
La ricerca delle vulnerabilità non si limita all’analisi delle singole componenti, ma vengono prese in considerazione anche tutte le dipendenze transitive che utilizzano a loro volta.
Considerando che:
- le vulnerabilità insite nelle versioni delle componenti OSS vengono scoperte man mano nel tempo dalla community;
- nella maggior parte dei casi è sempre disponibile una versione più recente della componente che risolve le vulnerabilità precedentemente incluse;
l’uso di tool di dependency scan, possibilmente con un basso tasso di falsi positivi (come Sonatype Nexus IQ Lifecycle), risulta fondamentale per elevare gli standard di sicurezza dei software ed evitare potenziali attacchi hacker.
Secondo l’ultimo report “State of the Software Supply Chain” di Sonatype, se consideriamo una componente OSS Maven vulnerabile nel repository “Maven Central”, si stima che il 96% delle volte è già disponibile una versione con il fix e dunque sicura. Questo significa che solo il 4% (percentuale trascurabile) dei problemi di sicurezza inerenti a queste componenti è effettivamente inevitabile.
Le versioni di componenti vulnerabili dovranno dunque essere aggiornate a versioni più recenti che risultano sicure ed affidabili.
L’upgrade delle versioni dei componenti potrebbe sembrare a prima vista un ulteriore effort per il team di development, rendendo meno agile gli sviluppi.
Tuttavia, esiste sempre un trade-off tra la rapidità di sviluppo e lo sviluppo di un software sicuro e di qualità. Se vogliamo colmarlo dobbiamo investire nell’automazione e adottare best practice nella scelta delle versioni delle componenti da aggiornare (es. evitando versioni di pre-release, ma preferendo le versioni maggiormente adottate dalla community).
Code-Review e DevSecOps
Per ridurre al minimo la probabilità di introdurre vulnerabilità di sicurezza, è strettamente consigliato utilizzare rigide policy di code-review.
Se il codice viene revisionato durante il processo di integrazione delle modifiche, può essere bloccata la richiesta di merge (pull request) nel caso in cui vengano identificate:
- Versioni di dipendenze utilizzate che presentano vulnerabilità note;
- Bad practices nel codice che comportano l’introduzione di falle di sicurezza;
- Codice malevolo introdotto intenzionalmente dagli hacker nelle commit.
Specialmente se si tratta di un progetto open-source, il rischio di caricare (nei repository target) versioni di componenti vulnerabili, sarà così ridotto al minimo.
La revisione può avvenire sia manualmente tramite un esperto (es. sviluppatore senior) che analizza il codice e stabilisce se è idoneo, che automaticamente tramite pipeline di CI/CD.
È possibile introdurre in una pipeline di Continuous Integration (CI) appositi stage per effettuare:
- l’analisi delle dipendenze di progetto (Dependency Scan) utilizzando tool dedicati;
- l’analisi della qualità del codice (Quality Scan), utile per stabilire se il codice è mantenibile e non usa bad-practice che potrebbero potenzialmente mettere a rischio il software;
- l’analisi statica del codice sorgente alla ricerca di falle di sicurezza (Static Application Security Testing – SAST), basate sulla top ten di OWASP e su altri criteri.
Esistono molti altri controlli di security che è possibile integrare in pipeline, tuttavia, quelli sopra citati sono sicuramente i più comuni.
Il risultato è quello che si definisce DevSecOps, rendendo la software supply chain robusta alle vulnerabilità, intervenendo tempestivamente con operazioni di remedation mirate.
Software Bill of Material (SBOM)
In USA sono entrate in vigore norme che hanno reso la SBOM (Software Bill Of Material) obbligatoria in diversi ambiti. Ad esempio, risulta obbligatorio per i fornitori che vendono software al governo federale e a produttori di dispositivi medici.
La SBOM è tipicamente un file in formato CycloneDx / SPDX, contenente tutti i dettagli del software a cui fa riferimento, tra cui:
- un elenco dettagliato di tutte le componenti utilizzate, inclusi framework, librerie, moduli e altre dipendenze;
- l’origine delle componenti, indicando se sono sviluppate internamente, derivate da software open-source o provenienti da terze parti;
- le informazioni sulle licenze associate a ciascuna componente;
- collegamenti e informazioni relative a documentazione e risorse relative alle componenti di terze parti;
- eventuali altre informazioni e metadati rilevanti, come ad esempio note sulla sicurezza.
La SBOM risulta fondamentale per la cybersecurity ed è altamente richiesto in quanto fornisce tutti gli elementi per stimare il livello di rischio del software e gestire al meglio le eventuali minacce.
Anche in Italia lo SBOM sta diventando sempre più richiesto dalle PA.
Per fortuna, tool come Sonatype Nexus IQ Lifecycle, consentono la generazione automatica dello SBOM di tutti i software nel parco applicativo con relativamente pochi passaggi.
Analisi dipendenze con Sonatype Lifecycle
Prendendo in considerazione Sonatype Nexus IQ Server (licenza Lifecycle) come tool di dependency scan, vedremo come è possibile utilizzarlo ed integrarlo nella nostra tool-chain software.
Estensione browser & Nexus IQ IDE Integration
L’analisi delle dipendenze può essere effettuata dai developer sulle loro macchine, in modo da ottenere uno short feedback loop già nelle prime fasi iniziali di sviluppo e di scelta delle componenti.
Tramite l’estensione Chrome “Sonatype Nexus IQ Evaluation” è possibile identificare versioni di componenti con vulnerabilità direttamente durante la navigazione sui siti web relativi ai repository pubblici (es. Maven Central, NPMJS.COM, ecc.).
Inoltre, nella maggior parte degli IDE di sviluppo (es. Eclipse, IntelliJ IDEA, Visual Studio, VS Code) esiste un’estensione installabile che notifica al developer la presenza di versioni di componenti vulnerabili e/o non affidabili.
Nexus IQ Web UI & CLI
Utilizzando la web UI, la scansione delle dipendenze, è possibile avviarla manualmente oppure schedularla, anche ad intervalli periodici.
In alternativa, può essere eseguita tramite la Sonatype IQ CLI, tramite il seguente comando:
nexus-iq-cli \
–-application-id TestApp \
–-server-url http://localhost:8070 \
--authentication <nexus-user>:<nexus-pass> \
--stage develop \
<application-target-path>
Code language: Bash (bash)
Per ottenere l’Application ID è necessario censire la nostra applicazione nella piattaforma Nexus IQ, associandola ad un’organizzazione.
Nexus IQ consente di bollare l’analisi effettuata in uno dei seguenti stage: develop, build, release, operate. Se non indicato come argomento, viene scelto automaticamente lo stage di “build”.
Il target dell’applicazione corrisponde al path della cartella di progetto oppure ad un package / archivio dove è compressa l’applicazione o sono presenti i compilati.
Di seguito alcuni esempi di applicazioni target supportate in base al linguaggio di programmazione:
- Java, utilizzando pacchetti .jar/.ear/.war (prodotti post-build);
- NPM (necessario: package.json e package-lock.json / yarn.lock);
- Python (necessario: file requirements.txt / file relativi a Poetry);
- Elenco completo: https://www.sonatype.com/products/language-and-package-support.
Nexus IQ può essere integrato anche nei package manager come plugin. Ad esempio, su Maven è sufficiente aggiungere nel pom.xml il seguente contenuto:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>com.sonatype.clm</groupId>
<artifactId>clm-maven-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<serverUrl>http://localhost:8070</serverUrl>
<applicationId>test</applicationId>
<stage>develop</stage>
<serverId>nexus-site</serverId>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Code language: HTML, XML (xml)
Le informazioni relative all’autenticazione vengono invece memorizzate nel file settings.xml come da esempio (in chiaro oppure facendo riferimento ad opportune variabili di ambiente configurate):
<servers>
<server>
<id>nexus-site</id>
<username>${env.NEXUS_USER}</username>
<password>${env.NEXUS_PASS}</password>
</server>
</servers>
Code language: HTML, XML (xml)
ed eseguire la scansione delle dipendenze con il comando:
mvn package clm:evaluate -Dmaven.test.skip=true
Code language: Bash (bash)
Se invece utilizzate Gradle come package manager, un plugin equivalente è disponibile su: https://github.com/sonatype-nexus-community/scan-gradle-plugin.
Nexus IQ CI/CD Integration
Se rendiamo disponibile la CLI di Nexus IQ nell’agent/runner utilizzato da una pipeline di CI/CD, è possibile utilizzarla in uno specifico stage, generalmente posizionato dopo quello di build.
Nel caso il job definito in tale stage vogliamo sia gestito all’interno di un container, è possibile utilizzare un’immagine Docker ufficiale con la CLI a bordo, recuperabile da DockerHub.
Di seguito un esempio di job compatibile con una pipeline GitLab:
iq_policy_eval:
stage: test
image: sonatype/gitlab-nexus-iq-pipeline:latest
script:
- /sonatype/evaluate -i ${CI_PROJECT_NAME} target/*.war
artifacts:
name: "policy-eval-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
paths:
- ${CI_PROJECT_NAME}-policy-eval-report.html
Code language: YAML (yaml)
che permette in aggiunta anche il salvataggio del report di analisi come job artifact.
In questo caso le credenziali di autenticazione verranno recuperate dalle seguenti variabili di ambiente (definite come variabili di CI):
- NEXUS_IQ_URL;
- NEXUS_IQ_USERNAME;
- NEXUS_IQ_PASSWORD.
Su Jenkins, invece, è disponibile un’estensione ufficiale, scaricabile gratuitamente dal seguente link: https://download.sonatype.com/integrations/jenkins/nexus-jenkins-plugin-3.19.1-01.hpi.
Una volta installato e opportunamente configurato, è possibile definire uno stage di dependency scan come da esempio:
stage('Dependency Analysis') {
steps {
nexusPolicyEvaluation(
iqApplication: NEXUS_IQ_APP_ID,
iqInstanceId: 'nexus-iq-server',
iqStage: 'build'
)
}
}
Code language: Groovy (groovy)
In alcuni casi, potrebbe essere utile salvare anche lo SBOM che, come accennato precedentemente, viene automaticamente generato da Nexus IQ Lifecycle.
Per ottenerlo è sufficiente utilizzare le REST API dedicata di Nexus IQ, aggiungendo il seguente scripted step:
withCredentials([usernamePassword(credentialsId: 'nexus-iq', usernameVariable: 'NEXUS_IQ_USER', passwordVariable: 'NEXUS_IQ_PASS')]) {
for(sbomType in ['cycloneDx', 'spdx']){
sh("curl -u ${NEXUS_IQ_USER}:${NEXUS_IQ_PASS} -X GET --header Accept:application/json ${NEXUS_IQ_URL}/api/v2/${sbomType}/1.4/${NEXUS_IQ_APP_ID}/stages/build -o target/sbom-${sbomType}.json")
}
archiveArtifacts artifacts: 'target/sbom-*.json', onlyIfSuccessful: true
}
Code language: Groovy (groovy)
Di seguito alcune screenshot che mostrano il risultato finale su Jenkins, utilizzando una semplice pipeline di CI e l’applicazione dummy “spring-petclinic”.
Dal report possiamo notare che l’applicazione analizzata presenta vulnerabilità ad alto rischio nelle versioni di diversi componenti. Sarà necessario aggiornarle per rendere l’applicazione più sicura.
Ad esempio, analizzando il grafico di version ranking del componente “jackson-core”, possiamo facilmente individuare la versione 2.15.3 come versione ottimale, priva di vulnerabilità.
Approfondimenti
- Blog di approfondimento sui temi DevSecOps a cura di Sonatype
- Estrai la SBOM da un’applicazione a tua scelta
- 9° State of the Software Supply Chain Report
- Legislazione in materia di SBOM
Per approfondire la metodologia DevSecOps e applicarla nella tua tool-chain software scrivimi.