Uno degli argomenti a favore dell'open source è che chiunque può leggere il codice. Ma quasi nessuno installa il software compilandolo da sé: si scaricano pacchetti, binari, immagini già pronte. Sorge allora una domanda semplice e scomoda. Come faccio a sapere che il file che ho scaricato corrisponde davvero al codice pubblico che avrei potuto leggere? Rilasci firmati e build riproducibili sono le due risposte complementari.
Il problema della fiducia nel binario
Tra il codice nel repository e il file sul tuo disco ci sono diversi passaggi: la macchina di chi compila, gli strumenti di compilazione, il server da cui scarichi, eventuali mirror. Ognuno è un punto in cui un aggressore potrebbe inserire modifiche. Se il binario contiene codice malevolo assente dal sorgente, leggere il sorgente non serve a nulla.
Le difese si collocano su livelli diversi:
| Livello | Domanda a cui risponde | Strumento |
|---|---|---|
| Integrità del download | Il file è arrivato intatto? | Checksum |
| Autenticità | Il file proviene davvero dal progetto? | Firma del rilascio |
| Origine del codice | Il tag corrisponde a ciò che i manutentori hanno approvato? | Tag e commit firmati |
| Corrispondenza sorgente-binario | Il binario deriva da quel sorgente? | Build riproducibile |
Checksum: utili ma insufficienti
Molti progetti pubblicano accanto ai file un elenco di hash, per esempio SHA-256. Verificarlo è immediato:
sha256sum -c SHA256SUMS --ignore-missing
Il checksum garantisce che il file non sia stato corrotto durante il download. Non garantisce però che sia autentico: se un aggressore controlla il server, può sostituire sia il file sia l'elenco degli hash. Per questo il checksum va accompagnato da una firma.
Rilasci firmati
Il progetto firma con una chiave privata l'elenco dei checksum o i file stessi. Chi scarica verifica la firma con la chiave pubblica del progetto, ottenuta in precedenza da una fonte indipendente.
gpg --verify SHA256SUMS.asc SHA256SUMS
Esistono anche approcci più recenti che legano la firma all'identità del sistema di build o dell'account del manutentore, registrandola in un registro pubblico di trasparenza, così da evitare la gestione di chiavi a lungo termine. Qualunque sia il metodo, i punti critici restano due:
- custodia della chiave: se viene rubata, le firme non valgono più nulla; va conservata offline o in un dispositivo hardware;
- distribuzione della chiave pubblica: deve essere pubblicata in più posti e restare stabile nel tempo, altrimenti gli utenti non hanno modo di distinguere una chiave legittima da una sostituita.
Tag e commit firmati
Git permette di firmare tag e commit. Un tag di rilascio firmato dimostra che quel punto della storia è stato approvato da chi possiede la chiave:
git tag -s v2.4.0 -m "Rilascio 2.4.0"
git tag -v v2.4.0
È una protezione contro la manipolazione del repository o dell'account sulla piattaforma di hosting. Molte piattaforme mostrano anche un'indicazione di verifica accanto ai commit firmati.
Build riproducibili
Una build è riproducibile quando, partendo dallo stesso sorgente, con lo stesso ambiente dichiarato, chiunque ottiene un file identico bit per bit. A quel punto il confronto diventa semplice: se il mio hash coincide con quello del binario ufficiale, il binario deriva da quel sorgente.
Ottenerla richiede di eliminare le fonti di variabilità più comuni:
- date e orari incorporati nel binario o negli archivi;
- percorsi assoluti della macchina di compilazione;
- ordine dei file negli archivi, che dipende dal file system;
- versioni non bloccate di compilatori e dipendenze;
- localizzazione e fuso orario dell'ambiente.
Una pratica diffusa è la variabile d'ambiente SOURCE_DATE_EPOCH, che molti strumenti rispettano per usare una data fissa invece dell'orario di compilazione. Diverse distribuzioni Linux lavorano da anni per rendere riproducibile gran parte dei loro pacchetti.
Da dove iniziare in un progetto
- Pubblicare checksum per ogni file di rilascio.
- Firmare i checksum e documentare come ottenere e verificare la chiave pubblica.
- Firmare i tag di rilascio.
- Compilare i rilasci in un ambiente automatizzato e dichiarato, non sul portatile di un manutentore.
- Bloccare le versioni delle dipendenze e degli strumenti.
- Verificare la riproducibilità compilando due volte in ambienti diversi e confrontando gli hash.
In sintesi
- Leggere il sorgente non basta se non si può verificare che il binario derivi da esso.
- I checksum garantiscono l'integrità, le firme l'autenticità.
- Tag e commit firmati proteggono la storia del repository.
- Le build riproducibili permettono a chiunque di confermare la corrispondenza tra sorgente e binario.