Attualmente sono un ingegnere di un progetto in fase di sviluppo. Un "modulo" su questo progetto offre la possibilità di autenticazione/autorizzazione dell'utente. Tuttavia, ci preoccupiamo che l'algoritmo di hashing delle password potrebbe non essere all'altezza della situazione (alias non BCrypt). (La cosa terribile non è del tutto sicura di cosa sia e da dove venga!).
Questo ovviamente deve cambiare e la patch è in programma. Dobbiamo aggiornare naturalmente tutti i nostri utenti di test perché le loro password useranno il vecchio metodo di hashing, non è un grosso problema, tutti i nostri utenti demo sono automatizzati sulla build, quindi aggiorna lo script. Ma la domanda successiva è se questo è un sistema di produzione con utenti attivi e non aggiornati, di tutti gli importi. Quale sarebbe la migliore pratica.
La tua opzione 1. è una cattiva idea: oltre ai motivi di User Experience/Public Relations che dichiari, stai anche dando una finestra agli aggressori per intercettare i token di reimpostazione della password e compromettere ogni account sul tuo server. Inoltre, non risolve il problema se hai anche un utente che è troppo pigro per accedere/aggiornare la propria password.
A prima vista, sia 2. che 3. mi sembrano a posto. Il tuo # 2 non è meno sicuro di quello che stai facendo ora, ma 2. significherebbe che devi continuare a supportare l'attuale accesso debole per sempre (o fare qualcosa del tipo "Dopo X mesi stiamo cancellando la tua password e ti costringiamo a fare un recupero "che rompe la trasparenza dell'utente di Nizza che desideri, quindi ignoriamola).
Consideriamo il caso in cui ci siano utenti nel DB che non accederanno mai più. Con entrambi 2. e 3. devi continuare a supportare l'attuale hash alg nella tua base di codice per sempre nel caso in cui effettuino il login, ma almeno 3. ha il vantaggio che essi (o meglio, tu) sono protetti contro attacchi di forza bruta offline se il tuo DB viene mai rubato.
Dato che dovrai mantenere la colonna "old style flag" per sempre, fatti un favore e rendila un int
non un bool
in modo che se dovessi mai aggiornare l'hash della password ancora una volta, puoi registrare su quale stile vecchio sono su.
AGGIORNAMENTO: è stata posta una domanda molto simile qui e costruita sulla discussione di questo thread.
Se riesci a fare l'opzione 3, non vedo perché dovresti anche considerare gli altri. È di gran lunga l'opzione migliore. Con questa opzione, la mia sensazione sarebbe quella di considerare l'utilizzo di due diversi sali, uno per il vecchio algoritmo e uno per quello nuovo con bcrypt. Sto immaginando un set come questo:
Il rovescio della medaglia è che dovrai mantenere la password in memoria qualche millisecondo in più (chi se ne frega) e avrai la ricerca della tabella extra su ogni accesso, praticamente per sempre, fino a quando la tabella separata non sarà vuota o fino a quando i vecchi account diventeranno abbastanza stantio che sei disposto a richiedere loro di reimpostare la password da soli.
Nota che, se il tuo VECCHIO schema è sottoposto a hash con il sale, non sarai in grado di usare lo schema n. 3, a meno che non conservi il sale SEPARATAMENTE.
Normalmente il sale viene memorizzato insieme all'hash e si utilizza il sale come input per la funzione hash: se non si utilizza esattamente lo stesso sale, si otterrà un output completamente diverso.
Se newhash (oldsalt + oldhash, newsalt), quindi, pur avendo la password corretta, non sarai in grado di ricreare oldhash (poiché non hai oldsalt) e non puoi generare l'hash finale. La stessa cosa vale per tutto ciò che ha parametri (ad es. Bcrypt ha un parametro "cost" - questo deve essere impostato durante la crittografia ed è incorporato nell'output, per l'uso durante la convalida della password).
ANCHE : come è stato detto da altri, se stai memorizzando che l'hash è "vecchio" o "nuovo" stile, considera invece di memorizzare lo "schema" - dove, ad es 0 è il "vecchio" e 1 è bcrypt (nota che non uso "nuovo" - è "nuovo" ora, non sarà "nuovo" per sempre!). Un modo comune per farlo è avere un marcatore all'inizio dell'hash (questo potrebbe già essere il caso!). bcrypt utilizza uno dei seguenti prefissi standard: "$ 2a $", "$ 2b $", "$ 2x" o "$ 2y $". A seconda dei possibili output del tuo "vecchio" algoritmo, potresti dover creare il tuo prefisso per contrassegnarli, oppure potresti riuscire a cavartela con "tutto ciò che non inizia con" $ "è il vecchio algoritmo.
E infine, poiché ovviamente sei preoccupato (giustamente!) Della sicurezza delle vecchie password, suggerirei di costringere tutti a cambiare la loro password, inviando loro istruzioni via e-mail con un token (NON! INVIARE UN LINK! Non vuoi i tuoi utenti che fanno clic su un link. Dì loro di accedere al solito posto). Quindi, chiedi il token E la loro password. Altrimenti, qualcuno che ha rubato una password in passato può cambiare la password e ottenere una "nuova" password valida.
FINALMENTE: avere una data di scadenza - se le password non vengono modificate entro questa data, dovrebbero essere invalidate. Questa data dovrebbe essere nell'e-mail e non troppo in futuro (una settimana? Dipende da quanto tempo impiegano i tuoi clienti a rispondere). Successivamente, dovranno passare attraverso le procedure di "reimpostazione della password".
Non so quale sia il tuo schema di codifica della password, ma se non è così male, è probabile che la struttura della password nel vecchio e nel nuovo formato sia diversa.
Ho già visto qualcosa del genere in un vecchio sistema BSD quando il sistema è passato da una tradizionale codifica password a una più sicura. Il nuovo è iniziato con una sequenza di caratteri che non poteva esistere nel vecchio schema, quindi ogni volta che un utente con una vecchia password ha effettuato l'accesso, la sua password in chiaro è stata convalidata utilizzando il vecchio metodo e silenziosamente rielaborata e archiviata nuovamente nel database delle password con il nuovo metodo. Dopo un mese, nessuna vecchia password era presente nel database, senza che l'utente finale notasse nulla.
Sarebbe da qualche parte tra il secondo e il terzo metodo.
So che in un vero sistema Web di produzione, ora le cose possono andare molto peggio, perché gli utenti possono aspettare settimane o addirittura mesi prima di riconnettersi. Ma (a seconda dell'attività reale) può essere mitigato dal fatto che un utente che non si connette da diversi mesi può aver dimenticato la sua password - oppure puoi dirgli che lo ha fatto ... Ciò significa che aspetterei un po ' più a lungo qui probabilmente 3 o 6 mesi e dopo quel tempo reimposterei tutte le password di vecchio stile su un valore proibito costringendo l'utente a reimpostare la sua password sulla connessione successiva ... attraverso la schermata password dimenticata.
Le cose belle qui sono:
Il rovescio della medaglia è che ti costringe a implementare contemporaneamente sia il metodo di autenticazione + un aggiornamento automatico di tutta la password di stile.
Non hai menzionato la lingua che stai utilizzando. Php ha i suoi problemi con varie funzioni che dovrebbero restituire false quando dovrebbe restituire true in alcuni casi, o altre funzioni che sembrano fare il lavoro ma mancano della logica per gestire effettivamente tutti gli input possibili validi possibili.
Ma questo è il modo corretto di fare ciò di cui stai parlando in php anche se non stai usando php. Il codice di alto livello può lasciarti con un punto di partenza per codificarlo per i tuoi scopi.
http://php.net/manual/en/function.password-needs-rehash.php
$password = 'rasmuslerdorf';
$hash = '$2y$10$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS';
// The cost parameter can change over time as hardware improves
$options = array('cost' => 11);
// Verify stored hash against plain-text password
if (password_verify($password, $hash)) {
// Check if a newer hashing algorithm is available
// or the cost has changed
if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
// If so, create a new hash, and replace the old one
$newHash = password_hash($password, PASSWORD_DEFAULT, $options);
}
// Log user in
}
Quello che vorrei fare sarebbe il tuo numero 2 con ciò che è stato menzionato usando un an int. Dalla lettura della documentazione su PASSWORD_DEFAULT potrebbe cambiare quando vengono trovati algoritmi migliori e devono rimuovere quello attuale per motivi di insicurezza man mano che php viene aggiornato.