Le password di hashing con bcrypt e una costante non sono più sicure di SHA1?

3

Ho creato un'API che consente agli utenti di inviare dati crittografati per essere archiviati nel cloud. Per recuperare i loro dati, l'API richiede all'utente di fornire solo l'ID oggetto MongoDB che gli viene fornito quando ha inviato i propri dati, più l'hash SHA1 della password che hanno usato per crittografare i dati. Ho scelto di utilizzare sia l'ID oggetto che l'hash della password come chiave univoca per impedire agli utenti di richiedere dati sull'API, poiché ID oggetto diversi non sono complessi e facili da indovinare.

Ora ho passato il metodo hash da SHA1 a bcrypt in quanto è più sicuro, ma questo ha presentato un problema. Poiché bcrypt genera sempre un sale diverso, l'hash è sempre un significato diverso Non posso più utilizzare l'hash generato per identificare i dati dell'utente nel database, a meno che non costringa l'app a utilizzare lo stesso salt ogni volta che genera un hash bcrypt. Nota: se utilizzo una costante salt, sarà nota al pubblico in quanto il codice sorgente è open source.

La mia domanda è, se uso una costante salt con bcrypt per generare hash delle password, questo rende gli hash più facili da decifrare che usare SHA1 come ero originariamente, e se non è ancora un approccio poco pratico alla creazione di hash delle password sicure ?

In alternativa, esiste un approccio migliore per specificare una chiave univoca per i dati dell'utente nel db senza chiedere all'utente ulteriori informazioni?

Modifica: nel tentativo di mantenere la domanda il più semplice possibile, mi sono reso conto che non ho davvero fornito un contesto sufficiente per il motivo per cui ho scelto l'approccio che ho, quindi ecco un po 'di più informazioni per sperare che le cose siano più chiare e che le persone possano fornire una risposta il più precisa possibile.

L'intenzione è creare un servizio sicuro e anonimo per supportare l'app, che può essere eseguito da chiunque, ovunque. L'app e l'API sono open source e quindi chiunque desideri fornire il proprio spazio server per altri utenti dell'app può eseguire la propria istanza dell'API. Gli utenti dell'app possono quindi indirizzarlo all'URL dell'API di loro scelta, ciò consente loro di utilizzare qualsiasi istanza dell'API ospitata dal pubblico o persino ospitare un'istanza sul proprio server che solo loro possono utilizzare.

Poiché i dati dell'utente potrebbero essere seduti su qualsiasi server pubblico, è importante che i dati siano crittografati sul lato client e che l'utente non invii mai la password cleartext sul cavo, nel caso in cui il proprietario del server API (o chiunque altro) desideri per decifrare i dati utente ospitati. Il servizio è anonimo per ragioni di sicurezza e convenienza, quindi l'utente dell'applicazione non deve fornire un indirizzo email o altre informazioni identificative, basta fare clic su un pulsante nell'app e i loro dati sono crittografati con la loro password, che viene inviata a l'API con la loro password con hash:

Esempio di post del corpo:

{
    "data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
    "passwordHash":"d4de34f15aeadfb34bdf1fbbd57134b2baeb142c"
}

Risposta:

{
    "id":"507f191e810c19729de860ea",
    "lastUpdated":"2016-07-06T12:43:16.866Z"
}

La voce mongodb sarebbe simile a questa:

{
    "id": "507f191e810c19729de860ea",
    "passwordHash": "d4de34f15aeadfb34bdf1fbbd57134b2baeb142c",
    "data": "DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
    "lastUpdated": "2016-07-06T12:43:16.866Z"
}

Un utente può recuperare i propri dati con una richiesta GET a /api/data/{id}/{passwordHash} , quindi per l'esempio sopra:

/api/data/507f191e810c19729de860ea/d4de34f15aeadfb34bdf1fbbd57134b2baeb142c

Risposta:

{
    "data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
    "lastUpdated":"2016-07-06T12:43:16.866Z"
}

Poiché il valore id (ID oggetto MongoDB) non è eccessivamente complesso, un utente malintenzionato può potenzialmente indovinare qualcun altro id e recuperare i propri dati tramite l'API. Per questo motivo ho deciso di utilizzare l'id insieme all'hash della password dell'utente come chiave per rendere più difficile per l'utente malintenzionato indovinare la chiave univoca di qualcun altro. Tuttavia, se avessi usato solo l'ID oggetto come chiave e anche se potevano indovinare l'id di un altro utente, avrebbero recuperato solo i loro dati crittografati (non l'hash della password), che avrebbero quindi dovuto decodificare prima di leggere i dati. Questa sarebbe un'alternativa migliore per memorizzare l'hash della password? Ironia della sorte, questo sarebbe quindi rispondere al problema bcrypt come se non stavo memorizzando gli hash delle password sul server, non avrei bisogno di hash la password (utilizzata per crittografare i dati lato client) a tutti ...

Quindi, in conclusione, la domanda relativa alla mia app / api è molto più complessa della domanda, ma non posso davvero pensare a come inquadrarla correttamente (forse ho bisogno di cambiare la domanda ). Tuttavia, se qualcuno ha qualche opinione sugli aspetti di sicurezza del mio approccio dati i requisiti, sarei molto grato per l'input.

    
posta nero120 11.07.2016 - 18:16
fonte

3 risposte

6

Se stai utilizzando una costante salt, non è bcrypt:

An important requirement of any bcrypt implementation is that it exploit the full 128-bit salt space.

- Schema di password adattabile per il futuro

Because bcrypt generates a different salt every time, the hash is always different meaning I can no longer use the generated hash to identify the user's data in the database

Certo che puoi - dato che hai già il sale dell'hash precedente, usalo quando generi il nuovo hash.

    
risposta data 11.07.2016 - 19:28
fonte
6

Disclaimar: Please note that this answer was written before more details were added to the question. Some points therefore no longer applies, while others still do.

Problemi con il tuo approccio

[...] of the password they used to encrypt the data.

Utilizzi le password come chiavi di crittografia? Non è una buona idea, dal momento che le password scelte dagli utenti hanno una bassa entropia e possono quindi essere forzate brute.

To retrieve their data, the API requires the user to supply [...] the SHA1 hash of the password [...].

L'utente deve inviare la password, non l'hash. Avere l'utente che invia gli hash in modo efficace rende l'hash la password, il che significa che si memorizzano le password efficaci (cioè l'hash) non protette. Cerca "passa l'hash" per ulteriori informazioni.

Ma come puoi decifrare anche se la password è stata utilizzata per la crittografia e ottieni l'hash solo dall'utente?

Because bcrypt generates a different salt every time, the hash is always different meaning I can no longer use the generated hash to identify the user's data in the database, unless I force the app to use the same salt each time it generates a bcrypt hash.

Perché dovresti usare l'hash per identificare i dati? Si utilizza l'ID oggetto MongoDB per quello. Utilizza la password e l'hash per l'autenticazione.

Se non si passa l'hash, ma si passa la password, è possibile utilizzare bcrypt con i singoli sali per l'autenticazione. Generare uno casuale per ciascun utente e memorizzarlo nel database. Quindi, quando l'utente invia una password, l'hash lo utilizza e confronta. Qualsiasi libreria che esegue bcrypt dovrebbe avere una funzione per questo.

Una descrizione di come farlo correttamente

Quando l'utente salva i suoi dati sul server, fornisce una password. Si genera un sale casuale unico e lo si memorizza nel database. Quindi si hash la password utilizzando bcrypt e salt per ottenere una chiave di crittografia. Questa chiave viene utilizzata per crittografare i dati.

Quando l'utente vuole accedere ai dati, fornisce di nuovo la password. Lo hai cancellato con bcrypt e il sale memorizzato per ottenere la chiave. Lo usi per decifrare i dati. Infine controlli un MAC per vedere che la decrittografia funziona davvero - se la password fosse sbagliata avresti ottenuto la chiave sbagliata, e quindi il MAC non corrisponderebbe.

Si prega di notare che qui ci sono un sacco di stampe fini a questo, quindi non prendere i due paragrafi precedenti come guida completa.

Finalmente, sei sicuro di volerlo fare da solo qui? Probabilmente esistono già soluzioni esistenti che fanno ciò che vuoi fare. Se programmate il vostro rischio e pericolo, commettete un piccolo errore e tutti i dati degli utenti vengono rubati.

    
risposta data 11.07.2016 - 19:24
fonte
2

Non dovresti usare neanche!

L'uso di un sale costante sconfigge lo scopo di usare bcrypt. Usa un sale variabile! Proprio come SHA1, è facilmente incrinato. È come chiedere: cosa c'è di più sicuro, una scatola di legno o una scatola in acciaio con tripla armatura a prova di termite con una porta di legno? Ottima spiegazione di Tom Scott.

IMHO il tuo codice è probabilmente vulnerabile all'iniezione SQL a causa degli odori del codice. Odora molto. Controlla il tuo codice per SQL injection, XSS, CSRF, ecc.

(tutti questi sono stati decrittografati.)

Addendum

Se si intende crittografare i dati, utilizzare un sistema di chiavi pubblica / privata. Tuttavia, questo impedirà a tutti tranne l'utente di vedere i dati, incluso l'amministratore. Non crittografare i dati con password - crittografare i dati con chiavi appropriate. Consiglierei RSA-2048 se le informazioni non sono così sensibili, RSA-4096 per la sicurezza garantita del foglio di alluminio (la NSA usa questo), e RSA-8192 se sai che le spie del governo tenteranno di decrittografare le informazioni dell'utente e / o migliaia di miliardi di dollari incideranno sulla sicurezza del tuo database.

Puoi ancora utilizzare l'hashing per identificare i dati dell'utente: basta aggiungere il sale con i dati e cancellare i dati utente con il sale.

Il client potrebbe generare una firma per verificare l'identità dell'utente e agire come un'autorità di certificazione. E invece di identificare personalmente le informazioni, potresti usare un UUID casuale. Ma essere in grado di identificare l'utente sconfigge lo scopo dell'anonimato.

    
risposta data 11.07.2016 - 18:24
fonte

Leggi altre domande sui tag