Quale tipo / lunghezza di colonna dovrei usare per archiviare una password con hash Bcrypt in un database?


318

Voglio archiviare una password con hash (usando BCrypt) in un database. Quale sarebbe un buon tipo per questo, e quale sarebbe la lunghezza corretta? Le password con hash su BCrypt sono sempre della stessa lunghezza?

MODIFICARE

Hash di esempio:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

Dopo aver eseguito l'hashing di alcune password, sembra che BCrypt generi sempre hash di 60 caratteri.

MODIFICA 2

Ci scusiamo per non aver menzionato l'implementazione. Sto usando jBCrypt .


Vedi anche il framework di hashing delle password PHP di Openwall (PHPass). È portatile e indurito contro una serie di attacchi comuni alle password degli utenti. Il tizio che ha scritto il framework (SolarDesigner) è lo stesso tizio che ha scritto John The Ripper e siede come giudice nel concorso Password Hashing . Quindi sa qualcosa o due sugli attacchi alle password.
jww

1
Se qualcuno cade in cerca di una soluzione per scrypt : la risposta di Gumbo si applica anche a scrypt. Ho applicato personalmente BINARY (64) in MySQL e in seguito mi ha permesso di verificare l'uguaglianza dei byte in Python.
Philippe Hebert,

Risposte:


369

Il formato modulare cripta per bcrypt è costituito da

  • $2$, $2a$O $2y$identificare l'algoritmo di hashing e formato
  • un valore di due cifre che indica il parametro di costo, seguito da $
  • a 53 caratteri valore lungo base-64-codificati (usano l'alfabeto ., /, 0- 9, A- Z, a- zche è diversa dalla base standard 64 Codifica alfabetico) costituito da:
    • 22 caratteri di sale (effettivamente solo 128 bit dei 132 bit decodificati)
    • 31 caratteri di output crittografato (effettivamente solo 184 bit dei 186 bit decodificati)

Pertanto, la lunghezza totale è rispettivamente di 59 o 60 byte.

Quando usi il formato 2a, avrai bisogno di 60 byte. E così per MySQL ti consiglio di utilizzare la CHAR(60) BINARYoBINARY(60) (vedi Il _bin e binari di regole di confronto per informazioni sulla differenza).

CHARnon è binario sicuro e l'uguaglianza non dipende esclusivamente dal valore in byte ma dalla fascicolazione effettiva; nel peggiore dei casi Aviene considerato uguale a a. Vedi The _binand binaryCollations per maggiori informazioni.


28
Attenzione: l'archiviazione come binaria (60) può causare comportamenti imprevisti per l'uguaglianza delle stringhe (tra le altre cose). In .NET questo può essere superato usando String.Equals (fromDataBaseBinary60string, typicalishString, StringComparison.InvariantCulture)
JHubbard80

8
Se si definisce la colonna come CHAR (60) SET DI CARATTERI latin1 COLLATE latin1_bin, ora si ottengono i vantaggi di un confronto di stringhe accurato senza la necessità di una colonna binaria.
Ben

2
@AndreFigueiredo SQL_Latin1_General_CP1_CS_ASè sconosciuto in MySQL. Ciò che è noto è latin1_general_cs.
Gumbo,

1
Mi piacerebbe avere una definizione qui per quello 2, 2ae 2ymedia per algoritmo di hash e il formato. Non ho potuto trovare una risposta facile con qualche ricerca.
Giovedì

2
@Neon Il problema è che potresti confrontare diversi hash per essere uguali. Se specifichi esplicitamente che si tratta di una colonna binaria (o di un VARCHAR con le regole di confronto corrette), non corri il rischio, da qualche altra parte, di modificare alcune impostazioni che lo rendono un confronto senza distinzione tra maiuscole e minuscole. Rende anche più chiaro il tuo intento, il che è generalmente una buona cosa: stai memorizzando dati binari; dovresti memorizzarlo come dati binari.
Finanzi la causa di Monica

52

Un hash Bcrypt può essere archiviato in una BINARY(40)colonna.

BINARY(60), come suggeriscono le altre risposte, è la scelta più semplice e naturale, ma se si desidera massimizzare l'efficienza di archiviazione, è possibile risparmiare 20 byte decostruendo senza perdita di hash. L'ho documentato più approfonditamente su GitHub: https://github.com/ademarre/binary-mcf

Gli hash di Bcrypt seguono una struttura denominata modular crypt format (MCF). Binary MCF (BMCF) decodifica queste rappresentazioni hash testuali in una struttura binaria più compatta. Nel caso di Bcrypt, l'hash binario risultante è 40 byte.

Gumbo ha fatto un buon lavoro nel spiegare i quattro componenti di un hash Bcrypt MCF:

$<id>$<cost>$<salt><digest>

La decodifica in BMCF è la seguente:

  1. $<id>$ può essere rappresentato in 3 bit.
  2. <cost>$, 04-31, può essere rappresentato in 5 bit. Metti insieme questi per 1 byte.
  3. Il salt di 22 caratteri è una rappresentazione (non standard) di base 64 di 128 bit. La decodifica Base-64 produce 16 byte.
  4. Il digest hash di 31 caratteri può essere decodificato in base 64 a 23 byte.
  5. Metti tutto insieme per 40 byte: 1 + 16 + 23

Puoi leggere di più al link sopra o esaminare la mia implementazione di PHP , anche su GitHub.


49
Costo del campo più lungo: 20 byte volte anche un milione + record: 20 MB, una volta raggiunto un milione di record +. Costo dell'implementazione impropria di una lunghezza ridotta del campo, in un campo di sicurezza e ingegneria molto complesso: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Fai la matematica.
Kzqai,

6
@Kzqai, come ho detto, la colonna più grande da 60 byte è la scelta più naturale, ma quanto aggressivamente perseguire l'efficienza dello storage dipende dal progetto. Ad esempio, è comune cercare di adattare l'intero database in memoria e 20 MB qui e altri 20 lì possono sommarsi rapidamente in un ambiente con memoria limitata.
Andre D,

10
Il tuo esempio alimenta il mio punto. --- Se vuoi mettere il tuo database in memoria, ottimizza ogni altra colonna prima di toccare la colonna di archiviazione bcrypt. --- Se hai ottimizzato tutte le altre colonne a livelli folli e solo la colonna hash di bcrypt è rimasta, ottieni un altro concerto di memoria solo per bcrypt. --- Se hai fatto entrambe le cose sopra ... ... stop, non hai ottimizzato tutte le altre colonne di frutta bassa e stai per rovinare un sistema di sicurezza crittografica testato che funziona e sostituisci con un sistema domestico più complicato con possibilità di fallimento dell'implementazione.
Kzqai,

11
@Kzqai Non vi è alcun rischio di indebolire la sicurezza della tua libreria Bcrypt qui. È una codifica dei dati che viene annullata al momento del recupero dalla memoria prima del controllo della password. Questo non è il territorio "non tirare il tuo cripto".
Andre D,

1
Bella spiegazione. :) Anche se la tua spiegazione ha dato un'ottima idea, voglio solo andare con 60 caratteri, anche 100 caratteri, solo per essere al sicuro. Bel dibattito anche @Kzqai e AndreD
Naveen Kumar V

23

Se stai usando PHP password_hash()con l' PASSWORD_DEFAULTalgoritmo per generare l'hash bcrypt (che suppongo sia una grande percentuale di persone che leggono questa domanda) assicurati di tenere presente che in futuro password_hash()potrebbe utilizzare un algoritmo diverso come predefinito e questo potrebbe quindi influisce sulla lunghezza dell'hash (ma potrebbe non essere necessariamente più lungo).

Dalla pagina del manuale:

Nota che questa costante è progettata per cambiare nel tempo man mano che nuovi e più potenti algoritmi vengono aggiunti a PHP. Per tale motivo, la lunghezza del risultato derivante dall'utilizzo di questo identificatore può cambiare nel tempo. Pertanto, si consiglia di archiviare il risultato in una colonna del database che può espandersi oltre 60 caratteri (255 caratteri sarebbe una buona scelta).

Usando bcrypt, anche se hai 1 miliardo di utenti (ovvero stai attualmente competendo con Facebook) per memorizzare hash password da 255 byte, solo ~ 255 GB di dati - circa le dimensioni di un piccolo disco rigido SSD. È estremamente improbabile che l'archiviazione dell'hash della password costituisca il collo di bottiglia nell'applicazione. Tuttavia, nel caso in cui lo spazio di archiviazione sia davvero un problema per qualche motivo, puoi usare PASSWORD_BCRYPTper forzare password_hash()l'uso di bcrypt, anche se non è l'impostazione predefinita. Assicurati di essere informato su eventuali vulnerabilità rilevate in bcrypt e di rivedere le note di rilascio ogni volta che viene rilasciata una nuova versione di PHP. Se l'algoritmo predefinito viene mai modificato, sarebbe opportuno esaminare il perché e prendere una decisione informata sull'opportunità di utilizzare il nuovo algoritmo o meno.


20

Non penso che ci siano trucchi accurati che puoi fare per archiviarlo come puoi fare ad esempio con un hash MD5.

Penso che la tua scommessa migliore sia quella di memorizzarla CHAR(60)come è sempre lunga 60 caratteri


Sebbene, la documentazione di PHP noti che le colonne dovrebbero essere in grado di contenere più dati, per le versioni future ...
Julian F. Weinert,

16
Nessun motivo per il piatto d'oro. Se il software in uso richiede sessanta byte, allocare sessanta byte. Se c'è una versione futura del tuo software che la modifica, puoi preoccuparti quando si verifica quella versione. Non dovresti installare automaticamente aggiornamenti che cambiano funzionalità.
Tyler Crompton,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.