BCrypt sul lato client, archivia sale e hash separatamente nel database MySQL [duplicato]

9

Sto sviluppando un'app per Android con un database MySQL per l'archiviazione delle credenziali di accesso dell'utente. Sto usando jBCrypt per le password di hashing prima di memorizzarle nel database. Al momento della registrazione, la password viene cancellata sul lato client come segue:

String salt = BCrypt.gensalt();
String hash = BCrypt.hashpw("password", salt).split("\$")[3];
salt = salt.split("\$")[3];
hash = hash.substring(salt.length(), hash.length());

In questo caso, BCrypt.hashpw() mi darà l'hash

$2a$10$rrll.6qqZFLPe8.usJj.je0MayttjWiUuw/x3ubsHCivFsPIKsPgq

Quindi rimuovo i parametri ( $2a$10$ ) e memorizzo i primi 22 caratteri come salt e gli ultimi 31 come hash nel database:

------------------------------------------------------------------------
|  uid  |          salt            |               hash                |
------------------------------------------------------------------------
|   1   |  rrll.6qqZFLPe8.usJj.je  |  0MayttjWiUuw/x3ubsHCivFsPIKsPgq  |
------------------------------------------------------------------------

Ora, ogni volta che un cliente vuole accedere, inserisce il nome utente e la password e solo il salt viene restituito dal database. Il client calcola il loro hash chiamando BCrypt.hashpw() con il loro sale:

String salt = "$2a$10$" + returnedSalt;
String hash = BCrypt.hashpw(“password”, salt).split("\$")[3];
hash = hash.substring(salt.length(), hash.length());

dammi:

hash = "0MayttjWiUuw/x3ubsHCivFsPIKsPgq"

che è uguale all'hash memorizzato nel database. Il client invia quindi il nome utente e l'hash calcolato al server. Se corrispondono, l'utente si collega.

So che posso semplificare questo processo recuperando l'intero hash di BCrypt direttamente e confrontandolo con la password fornita con

if (BCrypt.checkpw(“password”, bCryptHash))
  // match

ma è sbagliato inviare l'intera password con hash all'utente per eseguire il controllo.

Capisco che sia preferibile cancellare le password sul lato server, ma c'è qualcosa di sbagliato in questa soluzione? Mi manca qualcosa?

Supponiamo di avere una connessione HTTP non criptata tra il telefono e il server. Questa sarebbe una soluzione sicura?

    
posta yberg 08.03.2016 - 18:44
fonte

3 risposte

36

Il client è l'autore dell'attacco. Cammina nel tuo ufficio cantando quella frase 144 volte; assicurati di punteggiare la tua dizione con un piccolo tamburo. In questo modo, te ne ricorderai.

Nel tuo server, stai inviando codice Java per l'esecuzione sul client. Il cliente onesto eseguirà il tuo codice. Nessuno costringe l'aggressore a fare altrettanto; e si utilizza l'autenticazione del client proprio perché si teme che il client possa essere qualcun altro, che tenta di impersonare l'utente normale. Dal punto di vista del server, vengono visualizzati solo i byte che si inviano al client e i byte che ritornano. Non puoi assicurarti che questi byte siano stati calcolati con il tuo codice. L'attaccante, essendo un mascalzone malvagio, è perfettamente in grado di immaginare non di eseguire il tuo codice e invece di inviare la risposta che il tuo server si aspetta.

Considerate ora un semplice schema di autenticazione della password in cui semplicemente salvate le password degli utenti nel vostro database. Per autenticare, chiedere all'utente di inviare la password e quindi confrontarla con ciò che è stato memorizzato. Questo è molto semplice. Anche questo è molto brutto: si chiama password in chiaro . Il problema è che qualsiasi semplice occhiata di sola lettura al database (che si tratti di un attacco di SQL injection, un nastro di backup rubato, un vecchio hard disk recuperato da un dumpster ...) darà all'aggressore tutte le password. Per dichiarare chiaramente le cose, l'errore qui è di archiviare nel database esattamente i valori che, una volta inviati dal client, concedono l'accesso.

E il tuo schema proposto? Esattamente la stessa cosa Si archivia nel database il valore hash "così com'è". E quando il client invia quel valore esatto, l'accesso è garantito. Ovviamente, il client onesto invierà il valore mediante l'hashing di una password. Ma ammettiamolo: molti attaccanti non sono persone oneste.

Ora c'è un valore nel fare parte dell'hashing sul lato client. Effettivamente, un buon hashing della password è una corsa agli armamenti, in cui l'hashing è fatto di proposito a caro prezzo. Scaricare una parte del lavoro sui clienti può essere una buona cosa. Non funziona altrettanto bene quando i clienti sono deboli, ad es. smartphone con Java, o, peggio ancora, Javascript (che è una cosa completamente diversa, nonostante la somiglianza del nome).

In tal caso, è necessario eseguire bcrypt sul client e archiviare sul server non l'output bcrypt, ma l'hash dell'output bcrypt con una funzione di hash ragionevole (una veloce come SHA-256 andrebbe bene). L'elaborazione di una password P sarebbe quindi una bcrypt sul client, quindi un SHA-256 del risultato, calcolato sul server. Ciò spingerà la maggior parte della spesa della CPU sul client e sarà sicuro come un raw bcrypt per ciò che è destinato a fare (vedi sotto).

Comprendo che si desidera "crittografare" le password (l'hashing non è crittografia!) perché si desidera utilizzare un semplice HTTP. Non ti piace HTTPS per la stessa ragione di chiunque altro, che è il temuto certificato SSL. Pagare 20 dollari l'anno per un certificato SSL sarebbe simile alla rimozione della pelle con un pelapatate cosparso di succo di limone.

Sfortunatamente, non è possibile sfuggire al peeler. Come altri hanno osservato, anche se si dispone di un meccanismo di autenticazione solido, il raw HTTP non è ancora protetto e un attaccante attivo può semplicemente aspettare che un utente effettui l'autenticazione e dirottare la connessione da quel punto. Un esempio molto comune di "attaccante attivo" sono le persone che eseguono semplicemente un falso punto di accesso WiFi, ovvero un vero e proprio punto di accesso WiFi, ma mantengono anche l'opzione di dirottare le connessioni in qualsiasi momento. Questo è un tipo di modello di attacco che non può essere neutralizzato senza un protocollo crittografico completo che si estende su tutti i dati, non solo un metodo di autenticazione iniziale. Il tipo più semplice di tale protocollo è SSL / TLS. Qualsiasi protocollo che offra le stesse garanzie di cui hai assolutamente bisogno sarà anche complesso come SSL / TLS e molto più difficile da implementare perché, contrariamente a SSL / TLS, non è già implementato nel software client.

SSL è lì, basta usarlo. Per quanto riguarda il limone, succhialo.

(Se il costo finanziario è una barriera, ci sono alternative gratuite, come Let's Encrypt e pki.io . Resta da vedere se si adatta alla tua bolletta, ma vale la pena prenderlo in considerazione se sei davvero a corto di denaro.)

    
risposta data 08.03.2016 - 21:59
fonte
9

Se non utilizzi una connessione protetta (https), lo schema è difettoso in diversi modi:

  • La password con hash è tutto ciò che è necessario per accedere: sei vulnerabile a un attacco di riproduzione;
  • Dato che la connessione non è crittografata, il javascript che invii al client può essere manipolato dall'attaccante (l'utente malintenzionato potrebbe semplicemente lasciare che il client rispedisca a loro la password).

Se vuoi un accesso sicuro, devi essere in grado di garantire che il codice lato client sia corretto e che non ci sia nulla di valore da annusare sul filo.

In quasi tutte le circostanze, la soluzione migliore è utilizzare una connessione protetta SSL / TLS. È necessario inviare la password non hash su questa connessione sicura e eseguire l'hashing sul lato server.

Inoltre, rimuovendo la parte $2a$10$ , ti impedisci di aumentare il conteggio delle iterazioni ovunque nel futuro: poiché non memorizzi il conteggio dell'iterazione, non hai alcun metodo per identificare i vecchi hash da quelli nuovi dopo aver deciso di aumentare il conteggio delle iterazioni.

( Se puoi garantire il codice client, potresti teoricamente usare SRP ma farlo correttamente è un problema complesso ed è probabile che tu fallisca.

    
risposta data 08.03.2016 - 20:37
fonte
2

Se stai inviando qualcosa su una connessione HTTP non crittografata, supponi che sia guardato e possa essere compromesso con MITM.

Se qualcuno fosse MITM, potrebbe fornire il proprio sale e rispondere con il proprio hash. Pertanto, qualsiasi utente malintenzionato potrebbe completamente compromettere il processo di autenticazione.

Dovresti eseguire l'hashing del lato server delle password e utilizzare una connessione sicura.

    
risposta data 08.03.2016 - 18:51
fonte

Leggi altre domande sui tag