Vai al contenuto
Home » Come salvare la password dei propri utenti e vivere sereni un data breach

Come salvare la password dei propri utenti e vivere sereni un data breach

Ok, nessuno vive serenamente un data breach però ci sono accorgimenti che
possono mitigare parzialmente un’intrusione nel nostro backend.

La giornata parte alle 7 AM con una rassegna delle principali fonti sul web e,
come nei nostri peggiori incubi, il nostro nome è accanto ad un link su
pastebin dove viene sbandierato ai quattro venti il
nostro database degli utenti.

Effettivamente, hai avuto risvegli migliori.

I vari stadi della crisi

Ho provato a buttar giù, in ordine crescente, una lista di quanto sei nei guai.
Si va dal tanto al potresti essere quasi tranquillo.

Mettiamo un po’ qualche vincolo però. Se la tua applicazione web non da
all’utente dei vincoli di complessità alla quale la password deve sottostare
allora azzera tutto… potresti averla offuscata con qualsiasi algoritmo ma se
la password è “canebagnato12” o “pippopippo123” allora un attacco a forza bruta
impiegherà non più di qualche ora per indovinare la password.

Cosa farà l’attaccante

L’attaccante ti ha appena bucato, non importa per ora come1. Ha un file con
delle username e altre informazioni tra cui la password cifrata (ma averla in
chiaro è ahimé un’opzione) ed un salt (opzionale).

Le funzioni di hash, sono funzioni matematiche one way che prendono un
valore e lo trasformano in un altro valore, presumibilmente in un dominio
ristretto se confrontato a quellodi partenza.

Le funzioni di hash sono usate ad esempio per calcolare l’indice dove
memorizzare un dato in una struttura dati chiamata Hashmap.

Queste funzioni hanno un limite. Essendo l’alfabeto di destinazione più piccolo
del sorgente, nasce il fenomeno delle collisioni. La collisione è quando la
stessa funzione di hash, applicata a due input diversi, restituisce lo stesso
valore in output.

Gli algoritmi della famiglia RC, MD e SHA sono particolari funzioni di hash
studiate per minimizzare il problema delle collisioni. Se qualcuno pensa che
una funzione di hash è una funzione di crittografia, non sa di cosa sta
parlando. Ci sono algoritmi di offuscamento che sono anche funzioni di hash, ma
non è detto che tutte le funzioni di hash sia anche un algoritmo di
offuscamento. Eviterei di usare la parola crittografia… perché lo scopo della
crittografia è avere un sistema sicuro per lo scambio di informazioni che
permetta ai legittimi interlocutori di mettere in chiaro il testo. Le funzioni
di secure hashing non hanno questo obiettivo. Una volta offuscato, il dato
resta offuscato e l’unico modo di metterlo in chiaro è un attacco di forza
bruta o con dizionario.

Eccoci al dunque. L’attaccante ha una serie di hash, che non sono reversibili
per definizione. Nel suo arsenale d’attacco di saranno un bel po’ di dizionari
contenente password di uso comune alle quali verrà applicato l’algoritmo di
hash per vedere se il valore offuscato è nel database appena trafugato.

Il particolare algorito di hash viene desunto dalla lunghezza dell’hash stesso.
Quindi è un’informazione che dobbiamo dare per scontata sia in mano a chi ci
attacca.

Terminati i dizionari, si parte enumerando tutte le possibili combinazioni di
lettere e numeri. E qui è solo una questione di tempo (tranne in un caso).

Fuori concorso: hai creato un tuo meccanismo di cifratura

Esiste una regola aurea nel mondo dell’IT security. Mai, mai, mai
inventarsi un metodo di cifratura fatto in casa. Gli algoritmi in uso sono
stati elaborati da scienziati con profonde basi matematiche e sono stati
rivisti più e più volte da comitati fatti di matematici ed esperti di
criptoanalisi. Se tu te lo inventi da solo e poi dici di fare bagni di umiltà a
me che te lo faccio notare, forse è il caso che tu riveda la definizione stessa
di umiltà.

Questo è un fuori concorso, perché se tu hai giocato a fare il criptoanalista
in cerca di successo, dai pure per scontato che un attaccante vero con qualche
skill abbia già capito come funziona il tuo algoritmo rivoluzionario.

Fai scadere subito tutti gli account forzando un reset password e prepara un
post pubblico di scuse con su il cappello con le orecchie da asino. Se Sgarbi
fosse nella stanza con te sentiresti eccheggiare le parole: “capra, capra,
capra!”.

defcon 1: le tue password sono memorizzate in chiaro

Vai nella stanza insieme al tuo amico che si inventa gli algoritmi di cifratura
ed ascolta con lui il commento tecnico di Sgarbi al tuo operato.

Non ci sono tante giustificazioni nel salvare la password di un utente in
chiaro se non una serafica ignoranza. Essendo routine già pronte ed il web
pieno zeppo di esempi di come usare una cifratura X ad una password, volerla
salvare in chiaro è il semplice mix tra menefreghismo ed ignoranza.

Ingiustificabile.

Come può un utente scoprire se ha dato la password in mano ad un cialtrone
siffatto? Prova a seguire il link del reset password, se l’applicazione ti fa
scadere la password e ti da un link per fare il reset password allora hai
qualche speranza. Se ti arriva una mail con la password in chiaro nel corpo del
testo, rispondi alla persona citando Sgarbi. Poi mi cercherei il sito del
competitor.

Tira giù il sito e metti al suo posto una pagina di scuse dove ti dichiari un
cialtrone ed avvisi tutti che hai messo alla berlina delle password che sono
state magari usate anche in altri siti. Dici che, nonostante su linkedin tu sia
un senior, è la prima volta che scrivi una web application e giuri giuri giuri
non rifarai più una cappella del genere.

Poi rimetti in piedi il portale come dio comanda.

defcon 2: le tue password sono offuscate senza salt

Allora aggiungere un salt random nel momento in cui viene applicata la funzione
di hash alla password è sicuramente un’ottima cosa. Perturba l’input quanto
basta per rendere un attacco a dizionario inutile e un attacco a forza bruta
veramente difficile, visto che devi attaccare sia la password che il salt
quindi le combinazioni lievitano.

Certo è, che se il salt è nello stesso DB delle password e quindi finisce in
mano all’attaccante, aggiungerlo non rende la tua ricetta molto più gustosa,
anzi. Il salt va protetto e memorizzato in un luogo diverso dall’hash.

Se l’attaccante ha in mano anche l’hash allora il tuo livello di nervosismo è
legato all’algoritmo di cifratura utilizzato.
Se l’attaccante non ha in mano l’hash, diciamo che ragionevolmente puoi stare
un po’ più tranquillo.

In entrambi i casi, applicazione offline, password reset forzato e post di
scuse ai tuoi utenti (che di solito sono i tuoi clienti paganti).

defcon 3: le tue password sono offuscate con SHA1 o MD5 con il salt

Dunque MD5 e SHA1 erano il top… forse una decina d’anni fa. Il problema è che
lo sviluppatore medio ha sentito questi nomi all’Università e li ha associati
al nome password e non si è mai evoluto.

Wang e Yu, hanno pubblicato nel 2005 un paper che parla di come produrre una
collisione per
MD5 e SHA1
,
spiegato anche in questo sito
dove vengono portati anche dei scenari d’attacco basati sull’uso delle hash
nella firma di programmi.

Ne parla anche Shneier sul suo
blog
, MD5 e
SHA1 non devono essere usate per offuscare la password, per creare una
signature per un sorgente anzi, meglio… non devono essere più usate.

Scordatevi SHA1 e MD5 e mi spiace se questa affermazione forte vi possa far
pensare che io sia uno spocchioso arrogante, ma il fatto è che questi due
algoritmi di hash non sono sicuri. Period. E questo sottolinea come una
funzione di hash non per forza è sicura.

Detto questo… il problema delle collisioni deve convincervi a non usarli in
codice reale. In realtà un attaccante della collisione se ne importa
relativamente.

Cambiare da SHA1 a SHA256 o SHA512 equivale a cambiare… una costante. Quindi
lato sviluppatore, dovete solo preoccuparvi di cambiare la dimensione della
colonna del database.

MessageDigest md = MessageDigest.getInstance("SHA-1"
String text = "This is some text"
md.update(text.getBytes("UTF-8"
byte[] digest = md.digest

// SHA-256
MessageDigest md = MessageDigest.getInstance("SHA-256"
String text = "This is some text"
md.update(text.getBytes("UTF-8"
byte[] digest = md.digest

// SHA-512
MessageDigest md = MessageDigest.getInstance("SHA-512"
String text = "This is some text"
md.update(text.getBytes("UTF-8"
byte[] digest = md.digest

In questi esempi il salt va accodato al testo da mandare in pasto alla classe
MessageDigest

Diciamo che puoi anche non mettere offline il sito, però una pagina dove ti
scusi dell’incidente è dovuta. Forza il cambio password a tutti e cambia il
metodo di offuscamento… cambialo in SHA512 già che ci sei.

defcon 4: le tue password sono offuscate con SHA256 o SHA512 con il salt

Ok, ci sei quasi.
Il metodo di cifratura è quasi quello ottimale.

Per un attaccante un attacco a dizionario è sempre possibile nel caso di
password senza vincoli di complessità e un attacco di forza bruta se hai usato
un salt è veramente complesso.

Puoi stare quasi tranquillo.

Comunque comunica ai tuoi clienti quello che è successo e forza il cambio
password per tutti. A livello di codice puoi stare dove sei e non fare
modifiche, se però vuoi usare
bcrypt, bhé allora
sarebbe il top.

defconf 4 e mezzo: le tue password sono offuscate con bcrypt o hai usato oauth

Ok, ci siamo. Stai usando
bcrypt. Questo ti
mette al riparo dall’aumento della potenza di calcolo degli strumenti usati per
l’attacco e quindi sul fatto che gli attacchi a forza bruta diventeranno sempre
più veloci man mano che l’hardware crescerà in
potenza
.

Ti mette al sicuro sul fatto che un attacco a forza bruta abbia successo…
tendenzialmente sì. Se hai permesso cmq “pippopippo12” come password lì te la
sei andata a cercare, però.

Io farei una pagina in risposta che spiego cosa è successo ed il dettaglio
tecnico sul fatto che sia stato usato bcrypt con un salt generato con le API di
Bcrypt e memorizzato in un posto a parte. Suggerirei il cambio della
password ma non forzerei un reset massivo.

Checklist pre disastro

Ma prima di andare online, per evitare di bruschi risvegli, cosa è opportuno fare?
Vediamo una checklist possibile.

  • hai validato tutto l’input che viene passato al database?
  • hai utilizzato i
    PreparedStatement
    usando i placeholder per inserire i valori messi dall’utente?
  • hai usato BCrypt per offuscare la password nel DB?
  • hai applicato un salt generato in maniera sicura con le API di BCrypt?
  • hai memorizzato il salt in un database diverso da quello degli hash?
  • hai fatto un penetration test alla tua applicazione prima di andare online?

Off by one

Se ci avete fatto caso, a parità di algoritmo, quello che veramente vi salva è
avere dei vincoli di complessità alla password. Una password non deve essere
una parola presa da un dizionario, deve avere una lunghezza adeguata e deve
contenere il solito mix tra alfanumerici e caratteri speciali come %, $, £, &,
/, ! etc.. etc…

Io sono un fan delle password composte da più parole quindi permetterei
l’inserimento di una stringa arbitraria come password, tanto voi dovete
memorizzarne il valore offuscato da un’hash, quindi di lunghezza costante.

Sfido chiunque a trovare una password come “io ho un cagnolone bagnato come
password perché mi sento 31337”, con un attacco a forza bruta.

Nel vostro codice non inventatevi la crittografia ed usate bcrypt.

Per il resto, enjoy!

UPDATE: grazie alor per aver
segnalato il typo nella checklist… ovviamente i salt vanno messi in un posto
diverso dagli hash.

  1. in realtà capire come ti ha bucato per te è vitale. Quest’informazione è
    il primo punto dal quale applicare patch e fare hardening. Ovviamente prima di
    tornare on line un penetration test applicativo è molto più che suggerito. 

Tag:

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.