Uno dei possibili utilizzi dei certificati digitali, forse il più diffuso, è nell’autenticazione dei siti Web per garantire la sicurezza delle comunicazioni online: per proseguire nell’analisi delle possibili applicazioni dei certificati, e anche molto altro, è necessario stabilire le basi minime per quanto riguarda i meccanismi di crittografia utilizzati, limitandoci a descrivere dal punto di vista funzionale quanto indispensabile perché gli argomenti trattati siano chiari.
Indice degli argomenti
Certificati digitali e sicurezza delle comunicazioni: gli algoritmi di cifratura
Crittografare delle informazioni vuol dire renderle non intellegibili (nasconderle) a tutti eccetto che al destinatario della comunicazione.
Solitamente l’esigenza della crittografia nasce quando il mezzo su cui la comunicazione avviene non è proteggibile in maniera adeguata: è il caso di Internet dove le informazioni in viaggio tra il nostro computer e quello remoto transitano su decine di segmenti di rete controllati da entità a noi sconosciute. Ci serve quindi un modo per proteggere adeguatamente i dati così che anche quando la nostra comunicazione venisse catturata, non sia comunque “leggibile”.
Gli algoritmi di cifratura più immediati che ci viene in mente di usare sono quelli detti con chiave simmetrica (o condivisa), schematizzati di seguito.
Questa classe di algoritmi è molto ampia e comprende centinaia di rappresentanti. Solo per citare alcuni tra quelli in uso comunemente oggi: AES, DES e 3DES, CAMELLIA, RC4, BLOWFISH, SERPENT.
Ognuno ha le sue applicazioni e punti di forza o debolezza, ma tutti condividono lo stesso principio: il mittente e il destinatario devono scambiarsi la chiave affinché la cifratura/decifratura delle informazioni possa avvenire. Questo è un problema serio perché lo scambio deve avvenire su un canale sicuro, non certamente lo stesso su cui passano i dati cifrati che è insicuro per definizione.
Un’applicazione comune di questo processo, anche se non forse immediatamente evidente, sono i file zippati con password. È facile per me comprimere diversi file da trasferire ad un collega usando un programma per comprimerli ed aggiungere una password al file: quello che succede è che i file vengono compressi ma anche cifrati (ad esempio con AES usando come chiave un derivato della password inserita). Posso poi allegare il file ad una mail e spedirli sicuro che nessuno li potrà leggere, ma il destinatario deve conoscere la password (chiave di cifratura) che ho usato per lo zip. Pare evidente che non la posso scrivere nella mail che veicola il file[1] ma dovrò usare un mezzo diverso come il telefono o un incontro personale per comunicarla. È chiaramente una situazione scomoda da gestire, richiede un secondo canale di comunicazione protetto e un rapporto uno a uno con tutti i possibili destinatari.
La soluzione (pratica) è arrivata negli anni ’70 con l’ideazione dei sistemi di crittografia detti “in chiave pubblica”. Sebbene matematicamente richiedano una buona preparazione, se ci concentriamo sull’aspetto funzionale sono abbastanza semplici da digerire. In sostanza l’utente genera con un programma apposito sul suo dispositivo una coppia di chiavi, una che chiamerà pubblica e l’altra che sarà detta privata. La logica è semplice: tutto quello che viene cifrato con la chiave pubblica può essere decifrato unicamente con la relativa chiave privata, e viceversa.
Quindi se ognuno espone la propria chiave pubblica, per comunicare in maniera sicura è sufficiente per me recuperarla ed usarla per cifrare la comunicazione. Non c’è più bisogno dello scambio anticipato della chiave segreta, userò quella pubblica a cui viene data diffusione universale.
È un cambiamento radicale che consente un livello di interazione tra le parti prima impensabile ed è, come visto in un precedente articolo, la base delle comunicazioni coi siti web quando usiamo https: dal certificato del sito web estraggo la chiave pubblica e la uso per cifrare la comunicazione. Il sito ha la relativa chiave privata e può decifrare il traffico[2].
In questo caso è evidente dove andare a prendere la chiave pubblica che (è compresa nel certificato che mi viene presentato), ma dove si trova quella delle altre parti con cui vorrei scambiare messaggi, ad esempio, quella di un’altra persona? Non ci sono limiti tecnici alle modalità di distribuzione della chiave che alla fine altro non è che una sequenza di caratteri: può essere diffusa su un sito web (qui c’è la mia, se volete vedere come è fatta), può essere allegata alle nostre mail in uscita, può essere depositata in una directory aziendale o pubblica così che il mio programma di posta la recuperi automaticamente, se ad esempio voglio cifrare un’email. Posso semplicemente chiederla al destinatario che me la invierà come ritiene più comodo, magari in un allegato di WhatsApp o in una mail.
Facciamo attenzione a un paio di cose: se cifro i dati con AES ed una chiave, diciamo ABC123, con la stessa chiave posso ovviamente decifrarli, è quello che dovrà fare il destinatario. Quindi io che cifro potrò riavere il messaggio in chiaro partendo dai dati cifrati, pare ovvio. Con un algoritmo in chiave pubblica, se uso la chiave pubblica del destinatario per cifrare un messaggio non potrò ottenere il mio messaggio originale dai dati cifrati, in quanto la chiave privata (l’unica che può decifrare) è solo in possesso del destinatario. Quindi, un po’ meno ovviamente di prima, una volta che ho cifrato dei dati nemmeno io potrò più averli indietro (a meno che ne abbia mantenuta una copia ovviamente).
L’altra considerazione, che al momento può apparire bizzarra, è che se io cifrassi un messaggio con la mia chiave privata, chiunque avesse la mia pubblica, quindi potenzialmente tutti, potrebbe decifrare il messaggio. Ora, per quanto possa apparire inutile cifrare un messaggio che poi chiunque può decifrare e leggere, vedremo come questa cosa si rivelerà essenziale negli usi di questi sistemi crittografici.
Certificati digitali e sicurezza delle comunicazioni: gli algoritmi di hashing
Passiamo ora ad una classe di algoritmi differente, quelli di hashing.
Avendo introdotto in precedenza quelli di cifratura (encryption), iniziamo subito con l’aspetto che li differenzia. Gli algoritmi di cifratura sono funzioni invertibili, cioè consentono di cifrare un messaggio e decifrarlo ottenendo indietro il messaggio originale, supposto di conoscere la chiave.
Gli algoritmi di hashing sono invece delle funzioni non invertibili, diciamo a senso unico. In aggiunta, gli algoritmi di hashing non hanno una chiave da inserire, ma solo il messaggio originale che produce un’uscita, detta digest o semplicemente hash.
Ci sono anche altre proprietà interessanti, quali ad esempio il fatto che il messaggio (ad esempio un testo) in ingresso può avere una lunghezza qualunque, mentre il digest avrà sempre una lunghezza fissata, il che aprirà a delle implicazioni interessanti. Inoltre un cambiamento anche minimo del messaggio in ingresso, produrrà un output completamente differente. Lo schema logico di questi algoritmi diventa:
- MD5: 128 bit, 16 caratteri/bytes (6e6bc4e49dd477ebc98ef4046c067b5f)
- SHA1: 160 bit, 20 caratteri (1e4e888ac66f8dd41e00c5a7ac36a32a9950d271)
- SHA256: 256 bit, 32 caratteri (b133a0c0e9bee3be20163d2ad31d6248db292aa6dcb1ee087a2aa50e0fc75ae2)
- CRC32: 32 bit, 4 caratteri (ee3a5171)
- Adler32: 32 bit, 4 caratteri (03fc019d)
Se utilizziamo “Ciao”, con la C maiuscola, ad esempio, con MD5 verifichiamo che il digest risultante è del tutto diverso da quello indicato qui sopra:
- MD5 di “Ciao”, C maiuscola: 16272a5dd83c63010e9f67977940e871
Sempre usando MD5 possiamo calcolare l’hash di tutta la Divina Commedia:
- f8e80614f503a5c9496b8a95e1d5c273
Sempre 128 bit, anche se l’ingresso è molto più lungo del semplice “ciao”.
Questo comporta che se un numero infinito di possibili ingressi deve produrre un insieme finito di hash, ci saranno più messaggi diversi che la funzione trasforma del medesimo hash. Provo a renderla più chiara con un esempio. Inventiamoci un algoritmo di hashing chiamato VSA (Very Stupid Algorith) che produce un singolo numero in uscita tra 1 e 10 (resto intero della divisione per 10 per essere precisi), e che funziona sommando tutte le lettere in ingresso assumendo a=1, b=2, c=3, … e togliendo le decine: Applicandolo otteniamo:
Messaggio | Hash/digest |
abc | 1+2+3 => 6 |
aa | 1+1 => 2 |
cba | 3+2+1 => 6 |
ea | 5+1 => 6 |
adda | 1+4+4+1 => 10 => 0 |
ccbbccbbaa | 3+3+2+2+3+3+2+2+1+1 => 22 => 2 |
È evidente che ci sono diversi ingressi che producono il medesimo output, essendo questo limitato alle uniche 10 cifre tra 0 e 9. Gli algoritmi citati sopra non sono stupidi come questo, ma hanno comunque un limite alla dimensione dell’output e quindi prima o poi si possono verificare delle collisioni[3], cioè si possono trovare messaggi differenti in ingresso che producono lo stesso messaggio in uscita.
Quando abbiamo di fronte un hash in pratica non possiamo tornare indietro al messaggio originale per due motivi: l’algoritmo in sé non lo consente, è progettato a senso unico (diversamente da quelli di cifratura) ed inoltre non potrei comunque sapere se il messaggio trovato sia davvero quello originale.
Se dalla tabella sopra diciamo che vogliamo sapere quale fosse il messaggio originale relativo all’hash “6”, dovremmo procedere a calcolare con VSA gli hash di tutti i possibili ingressi, che abbiamo detto essere infiniti.
Se poi trovassimo per tentativi che “abc” si trasforma in “6”, avremmo vinto? No, perché anche altri portano allo stesso hash, quindi non avremmo certezza del fatto che il messaggio originale fosse proprio “abc” o che fosse di tre lettere.
Quindi collisioni e struttura matematica degli algoritmi di hashing ci garantiscono che siano funzioni “a senso unico”, o non invertibili. L’unica cosa che possiamo fare, ed è molto costosa dal punto di vista dei calcoli, è calcolare l’hash di tutti i possibili ingressi fino a che ne troviamo almeno uno che produca quell’hash.
Se le applicazioni di un algoritmo di cifratura sono abbastanza evidenti, un po’ meno lo sono quelle degli algoritmi di hash che, in due parole, tritano tutto l’ingresso e producono una manciata di byte in uscita. In realtà hanno un uso molto diffuso, come vedremo nell’articolo sulla firma digitale, ma un uso abbastanza chiaro possono averlo nella creazione di un sistema di accesso, ad esempio un sito web.
In tempi remoti si poteva pensare che un sistema di accesso avesse una tabella come la seguente per registrare le utenze:
username | password |
Priscilla | Elvis35 |
Alice | Ilovebob |
Bob | EveEveEve |
Quando l’utente Priscilla si presenta al sistema inserisce la password “Elvis35”, il sistema cerca la riga nella tabella e verifica se Priscilla conosce la password corretta. Va tutto bene fino a quando qualcuno non si impossessa di quella tabella, che dovrebbe essere protetta ma sappiamo che i problemi càpitano e nemmeno di rado.
A questo punto chi ha rubato la tabella conosce direttamente tutte le password e può impersonare gli utenti (anche su piattaforme differenti da quella compromessa se si usa sempre la stessa password!). Tuttavia se sostituiamo il campo della password, con l’hash MD5 della stessa, otteniamo una tabella così:
username | MD5_della_password |
Priscilla | 6ae72529d5069bf3ba2af2bf0796bb35 |
Alice | 498a6c0496f619b56a5dc7ab2b0127d4 |
Bob | 057f5e631d57978caa3cc8e63719f93a |
La procedura di accesso prende la password inserita da Priscilla, ne calcola l’hash MD5 e poi lo confronta con quello nella tabella. Se la password inserita è corretta il suo hash sarà quello che leggiamo sopra. Quindi stiamo registrando non tanto le password, ma un loro derivato facilmente calcolabile. Chi rubasse questa tabella non avrebbe un’informazione subito utilizzabile, ma dovrebbe prima “crackare gli hash”, cioè inserire dentro a MD5 tutti i possibili caratteri fino a trovare un insieme che ha l’hash specifico, ad esempio 6ae72529d5069bf3ba2af2bf0796bb35 per quello di Priscilla. Come vedremo a breve, è un lavoro estremamente più complesso e costoso.
Su questo specifico aspetto ci sarebbero altre considerazioni che esulano da questo articolo. Preme purtroppo sottolineare però che le tabelle del primo tipo, con le password in chiaro, sono ancora in uso in molti sistemi, specialmente siti web, e ne abbiamo prova ogni giorno quando vengono pubblicati i dati derivanti dai data breach. È una cosa inammissibile che nel 2020 si registrino ancora le password in chiaro, soprattutto avendo sistemi pratici per evitarlo, ma di bestie informatiche è pieno il mondo.
Crackare le password (o gli hash)
Con quello che abbiamo esposto possiamo dare un significato a questo titolo, spesso abusato. Iniziamo con la cifratura: crackarla significa trovare la chiave segreta usata quando abbiamo cifrato il messaggio, la stessa che dovrebbe avere il destinatario.
Ci sono diversi modi per farlo ad iniziare dal più ignorante, detto attacco a forza bruta (brute force attack). Perché ignorante? Perché non si fa alcun ragionamento se non far passare una per una tutte le chiavi di crittografia fino a che si trova quella giusta. Questo metodo garantisce che prima o poi si arrivi in fondo e la chiave verrà recuperata. Tuttavia i tempi di calcolo potrebbero non essere compatibili con il nostro problema o con la nostra stessa esistenza (migliaia di anni).
Un secondo attacco è quello che prevede di utilizzare un dizionario di termini da usare come password ed è quindi detto dictionary attack. Assume che il nostro utente abbia usato una parola, un nome o simile come password: banana, Elvis, brescia, juventus sono tutti candidati. Esiste un mondo legato alla creazione dei dizionari ed alla selezione di quelli più opportuni per ogni attività. Se la password che cerchiamo è in un dizionario, allora potremmo ridurre a minuti il tempo di ricerca invece che millenni: un ottimo risultato.
Una variante del precedente è quella degli attacchi ibridi: si prende un dizionario e si mutano le parole cambiando alcuni caratteri o aggiungendo/preponendo altri caratteri. Così da Elvis generiamo Elvis+2 numeri e proviamo Elvis01, Elvis02 e così via. Vuol dire moltiplicare, in questo caso per 100, i numeri dei tentativi da fare per ogni parola del dizionario, ma sono sempre una frazione rispetto a quelli richiesti da un attacco a forza bruta. Le sostituzioni trasformano “password” in “p@$$w0rd”, sostituendo le lettere con altre che assomigliano e che gli utenti usano per ricordarsi più facilmente la propria chiave.
Poi ci sono gli attacchi crittografici. Ogni algoritmo ha una sua struttura matematica e studiosi della materia ogni giorno la analizzano per scoprire se non ci siano delle vulnerabilità. Questo significa che è possibile venga scoperto un modo di non dover fare tutti i tentativi per indovinare la chiave, risparmiando del tempo. Oppure che un algoritmo contiene delle backdoor, delle falle logiche appositamente inserite dal creatore che permettono ad esso, che le conosce, di ottenere la chiave in modo semplice o addirittura immediato.
È quindi essenziale che quando si adotta un algoritmo di crittografia se ne utilizzi uno la cui logica è aperta e analizzabile da tutti, affinché si possa dire che, ragionevolmente, non contiene backdoor o falle logiche. È quello che è accaduto per l’algoritmo DES, che con gli anni è stato sottoposto a molte analisi che hanno portato a ritenerlo non più sicuro perché contiene delle falle che consentono di ridurre molto il numero di chiavi da provare.
Inoltre, la lunghezza stessa della sua chiave (56 bit), con la potenza di calcolo attuale, consente di esaurire i tentativi di un attacco brute force in relativamente poco tempo. Per questo gli algoritmi, sia di cifratura che di hashing, invecchiano e regolarmente (decenni) vanno sostituiti con altri adeguati al periodo. Lo stesso è avvenuto per MD5 e SHA1 che dopo decenni di attività sono stati pensionati in quanto la crittoanalisi ha consentito di evidenziarne alcune vulnerabilità che portano a ridurre i tentativi necessari a trovare una collisione.
La cosa che deve essere assolutamente chiara è che non bisognerebbe mai affidarsi ad algoritmi “black box”, ovvero non documentati ed analizzabili, in quanto potrebbero nascondere brutte sorprese. Procedere applicando la security through obscurity, cioè ritenere un sistema sicuro perché non si sa come funzioni, è un metodo che la storia ha relegato alla categoria delle pessime idee.
Per gli algoritmi di hashing vale all’incirca quanto esposto in termini di procedura di cracking, anche se in pratica non cerchiamo una chiave, che non c’è, ma un messaggio in ingresso che dia quel particolare hash in uscita. Tecnicamente andrebbero fatte delle precisazioni, così come per la derivazione delle chiavi per gli algoritmi simmetrici, ma andremmo fuori ambito.
Conclusioni
Alla luce di quanto abbiamo visto, possiamo ricavare alcune considerazioni mai ripetute abbastanza per quanto riguarda la selezione delle nostre password. Innanzitutto, usare una password sufficientemente lunga e complessa, dove per complessità intendiamo un buon mix di lettere numeri e simboli.
Questo richiederà, negli attacchi a forza bruta, un numero di tentativi praticamente irrealizzabile. La complessità comprende anche il non utilizzare password che siano reperibili in un dizionario, anche se con variazioni come un “!” alla fine o due numeri all’inizio.
Infine, dobbiamo considerare di non riusare la password su più sistemi o siti: se uno di questi fosse, sfortunatamente per noi, tra quelli che conserva le password in chiaro, la sua compromissione significherebbe la possibilità che si verifichi la violazione anche degli altri sistemi su cui siamo registrati. Proviamo a dare un occhio al sito Have i been pwned per sapere quante volte le vostre credenziali siano state rubate e se fossero o metto conservate sotto forma di hash o in chiaro.
Gli algoritmi di cifratura e quelli di hashing hanno delle caratteristiche e proprietà che li rendono la spina dorsale della protezione di molte attività che eseguiamo in rete ma non solo. Maneggiare propriamente i termini ed i concetti esposti in questo articolo costituisce la base per il prossimo che verterà sulle applicazioni pratiche del tutto.
NOTE
- In qualche caso arrivano effettivamente delle mail con allegati compressi con password, quindi cifrati, con la password scritta nel testo. Si tratta solitamente di virus che adottano per diffondersi questa tecnica. I programmi antivirus, infatti, non sono in grado di vedere il contenuto degli zip cifrati e, secondo la loro configurazione, potrebbero lasciarli passare. L’utente poi legge la chiave nel testo della mail che normalmente parla di qualche criterio di sicurezza per giustificare questa procedura, e dallo zip estrae il virus. ↑
- È una semplificazione. In realtà per ragioni di efficienza questo meccanismo si usa davvero ma solo per scambiare una chiave detta di sessione, che viene poi usata con un algoritmo tradizionale come AES. Alcuni algoritmi in chiave pubblica (come quello di Diffie e Hellman) non sono propriamente “di crittografia” quanto piuttosto “di scambio delle chiavi”. ↑
- Il fatto che potenzialmente due ingressi differenti generino lo stesso hash non è rassicurante. Ad esempio, è possibile che due documenti diversi di cui voglio verificare la firma (ne parleremo in un articolo dedicato), vengano verificati come “lo stesso documento”. Benché in realtà questo possa avvenire, in pratica dobbiamo considerare che solitamente applichiamo gli hash ad un contesto specifico, dove i documenti hanno una loro applicazione. Trovare una collisione con “ciao” potrebbe voler dire trovare un ingresso composto da decine e decine di pagine di caratteri casuali: ora, se quello che mi aspettavo era “una frase”, oppure “una fattura elettronica” allora potrò considerare il contenuto del documento e capire da solo o con meccanismi tecnici di consistenza interna, se il documento in sé è valido. Ad esempio, la fattura elettronica è un file XML con una struttura ben definita, non è possibile validare come tale un documento composto da una miriade di caratteri casuali. In breve, se pure posso trovare due documenti con lo stesso hash, uno dei due generalmente non avrà senso nel contesto d’uso. Per capire che genere di collisioni si è riusciti a trovare fino ad ora, è sufficiente vedere gli esempi sulla pagina Wikipedia di MD5 o questo interessante articolo. La soluzione definitiva a questo problema? Calcolare un hash con due algoritmi differenti, ad esempio, SHA256 e MD5: trovare due documenti che generino una collisione su entrambi gli algoritmi non è considerato possibile. Molti siti da cui si scaricano ad esempio dei programmi, che se modificati potrebbero contenere dei virus, indicano per ogni file diversi hash così da essere certi che quello scaricato sia esattamente il file originale. ↑