Aggiornamento dell'hash della password senza forzare una nuova password per gli utenti esistenti

32

Mantieni un'applicazione esistente con una base utenti consolidata. Nel corso del tempo si è deciso che la tecnica attuale di hashing della password è obsoleta e deve essere aggiornata. Inoltre, per ragioni di UX, non vuoi che gli utenti esistenti siano costretti ad aggiornare la loro password. L'intero hashing della password deve essere eseguito dietro lo schermo.

Assumi un modello di database 'semplicistico' per gli utenti che contiene:

  1. ID
  2. E-mail
  3. password

Come si fa a risolvere un simile requisito?

I miei pensieri correnti sono:

  • crea un nuovo metodo di hashing nella classe appropriata
  • aggiorna la tabella utente nel database per contenere una password aggiuntiva campo
  • Una volta che l'utente ha effettuato l'accesso utilizzando l'hash della password obsoleta, riempire il secondo campo della password con l'hash aggiornato

Questo mi lascia il problema che non riesco a distinguere in modo ragionevole tra gli utenti che hanno e quelli che non hanno aggiornato l'hash della password e quindi saranno costretti a controllarli entrambi. Questo sembra orribilmente imperfetto.

Inoltre, questo significa in sostanza che la vecchia tecnica di hashing potrebbe essere costretta a rimanere indefinitamente fino a quando ogni singolo utente ha aggiornato la propria password. Solo in quel momento ho potuto iniziare a rimuovere il vecchio controllo di hashing e rimuovere il campo del database superfluo.

Sto cercando principalmente alcuni suggerimenti per la progettazione, dato che la mia attuale 'soluzione' è sporca, incompleta e cosa no, ma se è necessario un codice reale per descrivere una possibile soluzione, sentiti libero di usare qualsiasi lingua.

    
posta Willem 09.11.2013 - 18:15
fonte

6 risposte

25

Suggerirei di aggiungere un nuovo campo, "hash_method", con forse un 1 per indicare il vecchio metodo e un 2 per indicare il nuovo metodo.

Ragionevolmente parlando, se ti interessa questo genere di cose e la tua applicazione è relativamente longeva (che apparentemente è già), questo probabilmente accadrà di nuovo, dato che la crittografia e la sicurezza delle informazioni sono un campo così in evoluzione, piuttosto imprevedibile . C'è stato un tempo in cui una semplice esecuzione di MD5 era standard, se si usava l'hashing! Quindi si potrebbe pensare che dovrebbero usare SHA1, e ora c'è salatura, sale globale + sale casuale individuale, SHA3, diversi metodi di generazione di numeri casuali pronti per la cifratura ... questo non si fermerà semplicemente, quindi potresti risolvilo bene in un modo estensibile e ripetibile.

Quindi, diciamo ora che hai qualcosa di simile (in pseudo-javascript per semplicità, spero):

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword());


if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword)
{
    // TODO: Learn what "hash" means
    return clearPassword + "H@$I-I";
}

Ora comprendendo che esiste un metodo migliore, devi solo dare un refactoring minore:

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword(), user.getHashingMethod());

if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword, hashMethod)
{
    // Note: Hash doesn't mean what we thought it did. Oops...

    var hash;
    if (hashMethod == 1)
    {
        hash = clearPassword + "H@$I-I";
    }
    else if (hashMethod == 2)
    {
        // Totally gonna get it right this time.
        hash = SuperMethodTheNSASaidWasAwesome(clearPassword);
    }
    return hash;
}

Nessun agente segreto o programmatore è stato danneggiato nella produzione di questa risposta.

    
risposta data 09.11.2013 - 19:03
fonte
42

Se ti preoccupi abbastanza di implementare il nuovo schema di hashing per tutti gli utenti il più rapidamente possibile (es. perché quello vecchio è veramente insicuro), c'è in realtà un modo per "migrare" istantaneamente ogni password.

L'idea è fondamentalmente di hash hash . Anziché attendere che gli utenti forniscano la password esistente ( p ) al prossimo accesso, si utilizza immediatamente il nuovo algoritmo di hash ( H2 ) sull'hash esistente già prodotto dal vecchio algoritmo ( H1 ):

hash = H2(hash)  # where hash was previously equal to H1(p) 

Dopo aver eseguito questa conversione, sei ancora perfettamente in grado di eseguire la verifica della password; devi solo calcolare H2(H1(p')) anziché il precedente H1(p') ogni volta che l'utente prova ad accedere con la password p' .

In teoria, questa tecnica potrebbe essere applicata a più migrazioni ( H3 , H4 , ecc.). In pratica, vorrai liberarti della vecchia funzione di hash, sia per motivi di prestazioni che di leggibilità. Fortunatamente, questo è abbastanza semplice: al prossimo login riuscito, calcola semplicemente il nuovo hash della password dell'utente e sostituisci l'hash hash-of-a-hash esistente con esso:

hash = H2(p)

Avrai anche bisogno di una colonna aggiuntiva per ricordare quale hash stai memorizzando: quello della password o del vecchio hash. Nel caso sfortunato di perdita di database, questa colonna non dovrebbe rendere più facile il lavoro del cracker; indipendentemente dal suo valore, l'autore dell'attacco dovrebbe comunque invertire l'algoritmo sicuro H2 piuttosto che il vecchio H1 .

    
risposta data 10.11.2013 - 09:34
fonte
8

La tua soluzione (colonna aggiuntiva nel database) è perfettamente accettabile. L'unico problema con esso è quello che hai già menzionato: il vecchio hashing è ancora usato per gli utenti che non sono autenticati dal momento del cambiamento.

Per evitare questa situazione, puoi aspettare che gli utenti più attivi passino al nuovo algoritmo di hash, quindi:

  1. Rimuovi gli account degli utenti che non si sono autenticati troppo a lungo. Non c'è niente di sbagliato nella rimozione di un account di un utente che non è mai venuto sul sito web per tre anni.

  2. Invia un'email ai restanti utenti tra coloro che non si sono autenticati per un po ', dicendo che potrebbero essere interessati a qualcosa di nuovo. Contribuirà a ridurre ulteriormente il numero di account che utilizzano l'hashing precedente.

    Fai attenzione: se disponi di un'opzione di non spam che gli utenti possono controllare sul tuo sito web, non inviare l'email agli utenti che l'hanno verificata.

  3. Infine, alcune settimane dopo il passaggio 2, distruggi la password per gli utenti che stanno ancora utilizzando la vecchia tecnica. Quando e se tentano di autenticarsi, vedranno che la loro password non è valida; se sono ancora interessati al tuo servizio, potrebbero ripristinarlo.

risposta data 09.11.2013 - 19:08
fonte
1

Probabilmente non avrai mai più di un paio di tipi di hash, quindi puoi risolverlo senza estendere il tuo database.

Se i metodi hanno lunghezze hash differenti e la lunghezza hash di ciascun metodo è costante (ad esempio, passando da md5 a hmac-sha1), è possibile indicare il metodo dalla lunghezza dell'hash. Se hanno la stessa lunghezza, è possibile calcolare l'hash utilizzando prima il nuovo metodo, quindi il vecchio metodo se il primo test ha avuto esito negativo. Se hai un campo (non mostrato) che dice quando l'utente è stato aggiornato / creato l'ultima volta, puoi utilizzarlo come indicatore del metodo da utilizzare.

    
risposta data 10.11.2013 - 00:30
fonte
1

Ho fatto qualcosa di simile e c'è un modo per dire se è il nuovo hash E renderlo ancora più sicuro. Sto indovinando che più sicuro è una buona cosa per te se ti stai prendendo il tempo per aggiornare il tuo hash; -)

Aggiungi una nuova colonna alla tua tabella DB chiamata "salt" e bene, usala come un sale generato dall'utente random. (praticamente impedisce gli attacchi dei tavoli arcobaleno)

In questo modo, se la mia password è "pass123" e random sale è 3333, la mia nuova password sarebbe l'hash di "pass1233333".

Se l'utente ha un sale sai che è il nuovo hash. Se il sale dell'utente è nullo, sai che è il vecchio hash.

    
risposta data 12.11.2013 - 21:34
fonte
0

Indipendentemente dall'ottima strategia di migrazione, se il database è già stato rubato (e non è possibile dimostrare in modo negativo che ciò non sia mai accaduto), le password archiviate con funzioni hash non sicure potrebbero già essere state compromesse. L'unica soluzione per questo è richiedere agli utenti di cambiare le loro password.

Questo non è un problema che può essere risolto (solo) scrivendo codice. La soluzione è informare gli utenti e i clienti sui rischi a cui sono stati esposti. Una volta che sei disposto ad aprirlo, puoi eseguire la migrazione semplicemente reimpostando la password di ogni utente, perché è la soluzione più semplice e sicura. Tutte le alternative nascondono il fatto di aver sbagliato.

    
risposta data 20.06.2018 - 10:35
fonte

Leggi altre domande sui tag