Integrazione di GitHub CodeQL e Checkov per la Sicurezza Completa: Un Approccio Pratico con GitHub Actions

banner

Nell’era dello sviluppo software rapido e della DevOps, la sicurezza non può essere un ripensamento, bensì un elemento fondamentale integrato nel ciclo di vita dello sviluppo software (SDLC). L’automazione dei controlli di sicurezza è cruciale per identificare le vulnerabilità nelle prime fasi del processo di sviluppo, riducendo i rischi e i costi d’intervento in produzione.

Gli strumenti chiave che vedremo nel corso di questo articolo sono:

  • GitHub CodeQL: Motore di analisi semantica per identificare vulnerabilità nel codice.
  • Checkov: Strumento di analisi statica per Infrastructure as Code (IaC).
  • GitHub Actions: Automazione del workflow di sicurezza nel tuo repository GitHub.
  • GitHub Security: Piattaforma integrata per la gestione della sicurezza del codice.

Questo articolo esplora l’integrazione di questi strumenti, utilizzando un progetto di esempio (codeql-checkov-quickstart) arricchito con vulnerabilità realistiche, tra cui SQL Injection e configurazioni infrastrutturali insicure, per dimostrare come identificarle e correggerle efficacemente.

Workflow di Integrazione

Per orchestrare l’integrazione di CodeQL e Checkov, impiegheremo un workflow di GitHub Actions progettato per attivarsi a ogni push di codice o pull request. Questo workflow automatizzato lancerà in sequenza due scansioni di sicurezza.

  1. Scansione CodeQL Approfondita: eseguirà un’analisi semantica del codice sorgente, puntando a vulnerabilità di sicurezza, errori di qualità del codice e potenziali backdoor.

  2. Scansione Checkov Estesa: scansionerà i file IaC (Dockerfile, Kubernetes, Terraform), ricercando configurazioni errate, violazioni delle best practices di sicurezza e potenziali punti deboli nell’infrastruttura.

Il file .github/workflows/security-scan.yml è il workflow di GitHub Actions che integra CodeQL e Checkov. Questo workflow è progettato per essere eseguito su un repository GitHub che contiene un’applicazione Python e file di configurazione IaC. Il flow del workflow è mostrato nella figura seguente.

Diagramma 1 - Flow GitHub Action Security Scan

Diagramma 1 - Flusso del workflow di GitHub Actions per la sicurezza.

Invito a visionare .github/workflows/security-scan.yml per una maggiore comprensione del workflow; inoltre, troverete sul file yaml, dove necessario, i commenti che spiegano ogni sezione.

Un esempio pratico

Un progetto dimostrativo con vulnerabilità comuni in Python, Docker, Kubernetes e Terraform mostra come questi strumenti identificano e aiutano a correggere le debolezze di sicurezza. Il progetto di esempio è disponibile su GitHub codeql-checkov-quickstart è in particolare il branch the-red-code/code-with-vulnerability-1 contiene il codice con le vulnerabilità.

Test delle Vulnerabilità

Prima di correggere le vulnerabilità, è possibile testarle per verificarne l’effettiva presenza e il loro impatto. Vedremo come testare la vulnerabilità di SQL Injection nell’applicazione Python e la vulnerabilità di esecuzione come utente root nel container Docker.

Correzione delle Vulnerabilità e GitHub Security

Quali sono gli strumenti e le funzionalità di GitHub che ci aiutano a correggere le vulnerabilità e a garantire la sicurezza del codice e dell’infrastruttura?

  • GitHub Security fornisce un’interfaccia centralizzata per la gestione degli avvisi di sicurezza, inclusi quelli generati da CodeQL e Checkov.
  • Gli sviluppatori possono visualizzare i dettagli delle vulnerabilità, assegnare priorità, monitorare lo stato di correzione e chiudere gli avvisi una volta risolti.
  • GitHub Security offre anche funzionalità di automazione, come l’apertura automatica di issue per le vulnerabilità rilevate e l’integrazione con strumenti di terze parti per la gestione delle vulnerabilità.
  • CodeQL fornisce esempi concreti di vulnerabilità e la loro correzione, offrendo al lettore una guida pratica all’uso degli strumenti.
  • Integrazione con GitHub Copilot: GitHub Copilot può assistere nella correzione delle vulnerabilità suggerendo patch di codice e fornendo spiegazioni dettagliate.

A seguire una serie di figure mostrano come GitHub Security visualizza le vulnerabilità rilevate da CodeQL e Checkov, e come gli sviluppatori possono interagire con gli avvisi per correggere le vulnerabilità.

GitHub mostrerà un security alert nella sezione “Security” del repository, dettagliando la vulnerabilità, il file e la riga di codice interessata, e fornendo raccomandazioni per la correzione.

Figura 1 - Code Scanning CodeQL

Figura 1 - Code Scanning: GitHub CodeQL individua una vulnerabilità di SQL Injection in app.py.

Figura 2 - Code Scanning CodeQL

Figura 2 - Code Scanning: Dettagli della vulnerabilità di SQL Injection individuata da CodeQL.

Checkov, che scansionerà i file Kubernetes, Terraform e Dockerfile, e riporterà diverse vulnerabilità e best practices non rispettate. I risultati saranno anche disponibili nel log di GitHub Actions, evidenziando i problemi individuati e fornendo suggerimenti per la correzione.

Figura 3 - Code Scanning Checkov

Figura 3 - Code Scanning: Checkov individua vulnerabilità e best practices non rispettate.

Esempio pratico: Vulnerabilità SQL Injection in Python

Il codice a seguire mostra un esempio di vulnerabilità di SQL Injection dell’applicazione Python di esempio. Prima di correggere le vulnerabilità, possiamo testarle per verificarne l’effettiva presenza e il loro impatto.

# ...
@app.route('/utente')
def get_utente():
    utente_id = request.args.get('id')
    db_connection = sqlite3.connect('database.db')
    cursor = db_connection.cursor()

    # Vulnerabilità SQL Injection (String formatting)
    query = "SELECT nome, email FROM utenti WHERE id = {}".format(utente_id)
    cursor.execute(query)
    utente = cursor.fetchone()

    db_connection.close()

    if utente:
        return f"Nome: {utente[0]}, Email: {utente[1]}"
    else:
        return "Utente non trovato", 404
# ...

Python 1 - Vulnerabilità di SQL Injection nell’applicazione Python.

Avviamo l’applicazione Python con il comando python app.py per testare l’endpoint /utente con un parametro id malevolo.

Per la verifica della vulnerabilità di SQL Injection, possiamo utilizzare un valore come '1 OR 1=1 ORDER BY 1' per id e accertare che l’applicazione restituisca dati non autorizzati.

http "http://127.0.0.1:5000/utente?id=1 OR 1=1 ORDER BY 1"

Console 1 - Test della vulnerabilità di SQL Injection nell’applicazione Python.

Qualora l’applicazione restituisca dati non autorizzati o il comportamento sia diverso da quello atteso, la vulnerabilità di SQL Injection è confermata.

HTTP/1.1 200 OK
Server: Werkzeug/3.1.3 Python/3.12.6

Nome: Anna Neri, Email: anna.neri@example.com

Console 2 - Risposta dell’applicazione Python alla vulnerabilità di SQL Injection.

Come possiamo vedere dalla Console 2, l’applicazione Python restituisce i dati dell’utente Anna Neri (il cui id è diverso da 1) nonostante il parametro id contenga un valore malevolo (1 OR 1=1 ORDER BY 1), confermando la presenza della vulnerabilità di SQL Injection.

Possiamo provare qualcosa di pià sofisticato, come '1 UNION SELECT nome, password_chiaro FROM utenti --' per verificare se l’applicazione espone dati non autorizzati.

http "http://127.0.0.1:5000/utente?id=1 UNION SELECT nome, password_chiaro FROM utenti --"

Console 3 - Test avanzato della vulnerabilità di SQL Injection nell’applicazione Python.

Qualora l’applicazione restituisca dati sensibili, come password in chiaro, la presenza di una vulnerabilità SQL Injection è confermata.

HTTP/1.1 200 OK
Server: Werkzeug/3.1.3 Python/3.12.6

Nome: Anna Neri, Email: passwordAnna

Console 4 - Risposta dell’applicazione Python alla vulnerabilità avanzata di SQL Injection.

Come possiamo vedere dalla Console 4, l’applicazione Python restituisce i dati dell’utente Anna Neri e la password in chiaro passwordAnna nonostante il parametro id contenga un valore malevolo (1 UNION SELECT nome, password_chiaro FROM utenti --), confermando la presenza della vulnerabilità di SQL Injection avanzata.

Correzione della Vulnerabilità di SQL Injection

Per correggere la vulnerabilità di SQL Injection nella funzione get_utente in app.py, dobbiamo sostituire la string formatting con le parametrized queries per prevenire l’iniezione di SQL. Ecco come possiamo modificare il codice per correggere la vulnerabilità (SQL InjectionParametrized Queries).

  #...
  
  # Correzione della vulnerabilità di SQL Inject utilizzando
  # le parametrized queries.
  query = "SELECT nome, email FROM utenti WHERE id = ?"
  cursor.execute(query, (utente_id,)) # Passa l'input come parametro
  utente = cursor.fetchone()
  
  #...

Python 2 - Correzione vulnerabilità di SQL Inject sul file app.py

Rammentate che la correzione della vulnerabilità di SQL Injection è solo un esempio e che la correzione delle vulnerabilità di sicurezza deve essere valutata caso per caso, in base alle esigenze dell’applicazione e del sistema.

Esempio pratico: Vulnerabilità di Docker

Il file Dockerfile che definisce l’immagine Docker per l’applicazione Python, soffre di alcune vulnerabilità comuni.

  • Nessun utente specifico: il container viene eseguito come root all’interno del container.
  • Porta 5000 esposta: anche se l’applicazione potrebbe non aver bisogno di essere esposta direttamente all’esterno in produzione, lasciarla aperta nel Dockerfile può essere un rischio se non gestito correttamente.
  • Copia di tutti i file: il comando COPY . . copia tutti i file del repository all’interno del container, inclusi file sensibili o non necessari per l’applicazione. Questo potrebbe esporre informazioni sensibili o aumentare le dimensioni dell’immagine Docker inutilmente.

Il file completo Dockerfile è mostrato a seguire.

FROM python:3.9-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY ../../draft .

EXPOSE 5000

CMD ["python", "app.py"]

Docker 1 - Dockerfile dell’applicazione Python che contiene vulnerabilità.

Per testare la vulnerabilità di esecuzione come utente root nel container, possiamo eseguire il comando podman build e podman run per verificare se il container viene eseguito come utente root. A seguire sono indicati i passaggi per testare la vulnerabilità.

Forse sarete più avvezzi a Docker che potete comunque utilizzare al posto di podman se preferite. Vi invito alla lettura dell’articolo Cos’è Podman: scopriamo l’alternativa a Docker.

# Build dell'immagine container dell'applicazione Python usando
# il Dockerfile vulnerabile.
podman build -f docker/Dockerfile -t docker.io/amusarra/codeql-checkov-quickstart $(pwd)

Console 5 - Build dell’immagine Docker vulnerabile.

# Esecuzione del comando `id` all'interno del container dell'applicazione Python
# per verificare l'utente con cui viene eseguito il container.
podman run --rm -it docker.io/amusarra/codeql-checkov-quickstart:latest id

Console 6 - Verifica dell’utente con cui viene eseguito il container Docker.

L’output del comando id all’interno del container ci mostrerà l’utente corrente e il gruppo di appartenenza. Qualora l’utente sia root, la vulnerabilità di esecuzione come utente root è confermata.

uid=0(root) gid=0(root) groups=0(root)

Console 7 - Esecuzione del container come utente root.

Correzione della Vulnerabilità di Docker

Per migliorare la sicurezza del Dockerfile, possiamo apportare alcune modifiche per mitigare le vulnerabilità individuate.

  • Esecuzione come utente non-root: aggiungiamo un utente non-root al Dockerfile e cambiamo l’utente di esecuzione per ridurre i privilegi del container.
  • Rimuovere EXPOSE 5000: se la porta 5000 non deve essere esposta direttamente, rimuoverla dal Dockerfile (la porta verrà comunque gestita dal sistema di orchestrazione come Kubernetes se necessario).

Il file Dockerfile corretto è mostrato di seguito.

FROM python:3.9-slim-buster

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .

# Create a non-root group
RUN groupadd -r appuser

# Create a non-root user and add it to the 'appuser' group
RUN useradd -r -g appuser appuser

# Change ownership of /app to the non-root user
RUN chown -R appuser:appuser /app

# Switch to the non-root user to run the application
USER appuser

CMD ["python", "app.py"]

Dockerfile 2 - Dockerfile corretto per l’applicazione Python.

Successivamente alla correzione del Dockerfile, possiamo creare una nuova immagine e testare l’output del comando id all’interno del container e verificare che l’utente sia appuser e non root.

# Creazione nuova immagine container dell'applicazione Python
# usando il nuovo Dockerfile (vedi Dockerfile 2)
podman build -f docker/Dockerfile -t docker.io/amusarra/codeql-checkov-quickstart:1.0.0 $(pwd)

Console 8 - Build dell’immagine Docker corretta.

# Esecuzione del comando id all'interno del container dell'applicazione Python
# per verificare l'utente con cui viene eseguito il container.
podman run --rm -it docker.io/amusarra/codeql-checkov-quickstart:1.0.0 id

Console 9 - Verifica dell’utente con cui viene eseguito il comando all’interno del container.

uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)

Console 10 - Verifica del comando id all’interno del container come utente appuser e non root.

Come possiamo vedere dalla Console 8, l’utente corrente all’interno del container è appuser e non root, confermando che la vulnerabilità di esecuzione come utente root è stata corretta.

Esempio pratico: Vulnerabilità di Kubernetes

Prendendo in considerazione il file deployment.yaml che definisce il deployment di Kubernetes per l’applicazione Python, possiamo identificare alcune vulnerabilità comuni.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-app
  template:
    metadata:
      labels:
        app: python-app
    spec:
      containers:
        - name: python-app
          image: docker.io/amusarra/codeql-checkov-quickstart:latest # Immagine Docker da buildare
          ports:
            - containerPort: 5000
          securityContext:
            privileged: true # Container in modalità privilegiata!
          resources: # Limiti di risorse MANCANTI! VULNERABILITA'
            { } # Nessun limite specificato!

Kubernetes 1 - File deployment.yaml con vulnerabilità.

  • Mancanza di limiti di risorse (resources: {}): la sezione resources è presente ma vuota. Questo significa che non sono stati definiti limiti di risorse (CPU e memoria) per il container. In un ambiente di produzione, questo è un rischio significativo.

    • Denial of Service (DoS) locale: un container senza limiti di risorse può consumare tutte le risorse disponibili sul nodo Kubernetes su cui è in esecuzione, causando il blocco di altri container o dell’intero nodo.
    • Vulnerabilità a monte: se l’applicazione ha una vulnerabilità che può essere sfruttata per causare un consumo eccessivo di risorse (es. un attacco DoS applicativo, una regular expression Denial of Service - ReDoS, etc.), l’assenza di limiti di risorse aggrava l’impatto della vulnerabilità, permettendo all’attaccante di saturare le risorse del nodo.
    • Instabilità e imprevedibilità: l’assenza di limiti rende l’allocazione delle risorse imprevedibile e può portare a comportamenti inattesi dell’applicazione e dell’intero cluster.
  • privileged: true: il container è configurato per essere eseguito in modalità privilegiata. Questo concede al container accesso a tutte le capabilities del kernel host, rappresentando un grave rischio di sicurezza.

  • image: docker.io/amusarra/codeql-checkov-quickstart:latest: utilizzo del tag latest per l’immagine Docker. Questo rende difficile tracciare la versione dell’immagine in produzione e può portare a comportamenti inattesi se l’immagine latest viene aggiornata.

Sull’ultimo punto vi rimando alla lettura dell’articolo Perché il tag “latest” non va utilizzato.

Correzione delle Vulnerabilità di Kubernetes

Per migliorare la sicurezza del file deployment.yaml per Kubernetes, possiamo apportare alcune modifiche per mitigare le vulnerabilità individuate.

  • Utilizzare tag specifici per le immagini Docker: sostituire image: docker.io/amusarra/codeql-checkov-quickstart con un tag specifico dell’immagine Docker per tracciare le versioni.
  • Rimuovere privileged: true: rimuoviamo il campo privileged: true dal file deployment.yaml per eseguire il container in modalità non privilegiata.
  • Assegnare limiti di risorse: aggiungiamo limiti di risorse (CPU e memoria) al container per garantire che non possa consumare più risorse di quelle assegnate.

Il file deployment.yaml corretto è mostrato di seguito.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-app
  template:
    metadata:
      labels:
        app: python-app
    spec:
      containers:
      - name: python-app
        image: docker.io/amusarra/codeql-checkov-quickstart:1.0.0 # Tag immagine specifico
        ports:
        - containerPort: 5000
        securityContext: # Security context NON privilegiato
          {} # Vuoto, significa che non è privilegiato
        resources: # Limiti di risorse DEFINITI! (CORRETTO)
          requests: # Richieste minime di risorse
            cpu: 100m    # 100 millicore CPU (0.1 core)
            memory: 64Mi  # 128 MiB di memoria
          limits:   # Limiti massimi di risorse
            cpu: 500m    # 500 millicore CPU (0.5 core)
            memory: 128Mi  # 128 MiB di memoria

Deployment 2 - File deployment.yaml corretto per il deployment dell’applicazione Python in un cluster Kubernetes.

La sezione resources ora contiene le chiavi requests e limits:

  • requests: definisce la quantità minima di risorse (CPU e memoria) che il container richiede per essere schedulato su un nodo Kubernetes. Il scheduler di Kubernetes userà queste richieste per decidere su quale nodo schedulare il container.
  • limits: definisce la quantità massima di risorse (CPU e memoria) che il container può utilizzare. Kubernetes farà in modo che il container non superi questi limiti. Se il container tenta di superare i limiti di memoria, potrebbe essere throttled (rallentato) o killed (terminato), a seconda della configurazione del cluster e del tipo di limite (CPU o memoria).

In questo esempio, abbiamo definito richieste di 100 millicore CPU e 64MiB di memoria, e limiti di 500 millicore CPU e 128MiB di memoria. Questi valori sono solo di esempio e dovrebbero essere adattati in base alle reali esigenze di risorse dell’applicazione. È importante monitorare l’utilizzo delle risorse dell’applicazione in ambiente di test e produzione per definire limiti e richieste appropriati.

Lascio a voi la correzione del file main.tf per Terraform per la creazione del bucket S3.

Dopo aver corretto i file ed effettuato un nuovo push o pull request, il workflow security-scan.yml verrà eseguito nuovamente. Questa volta dovremmo vedere un minor numero di vulnerabilità e nessuna vulnerabilità corretta sul deployment Kubernetes.

Conclusioni

L’integrazione di GitHub CodeQL e Checkov tramite GitHub Actions, potenziata da GitHub Security, rappresenta un approccio completo e automatizzato per la sicurezza del software. Combinando l’analisi semantica del codice, la scansione delle configurazioni infrastrutturali e una piattaforma centralizzata per la gestione della sicurezza, è possibile ottenere una copertura di sicurezza più ampia e identificare le vulnerabilità in diverse fasi del processo di sviluppo.

Post correlati

TheRedCode.it - Il mondo #tech a piccoli #bit

Partners

Community, aziende e persone che supportano attivamente il blog

Logo di Codemotion
Logo di GrUSP
Logo di Python Milano
Logo di Schrodinger Hat
Logo di Python Biella Group
Logo di Fuzzy Brains
Logo di Django Girls
Logo di Improove
Logo del libro open source
Logo di NgRome
Logo de La Locanda del Tech
Logo di Tomorrow Devs
Logo di Coderful
Logo di VueSchool

Non perderti gli ultimi aggiornamenti, iscriviti a TheRedCode Digest!

La tecnologia corre, e tu devi correre più veloce per rimanere sempre sul pezzo! 🚀

Riceverai una volta al mese (o anche meno) con codici sconto per partecipare agli eventi del settore, quiz per vincere dei gadget e i recap degli articoli più interessanti pubblicati sul blog

Ci sto!

Vuoi diventare #tech content creator? 🖊️

Se vuoi raccontare la tua sul mondo #tech con dei post a tema o vuoi condividere la tua esperienza con la community, sei nel posto giusto! 😉

Manda una mail a collaborazioni[at]theredcode.it con la tua proposta e diventa la prossima penna del blog!

Ma sì, facciamolo!