Come eseguire l'hashing di password lunghe (> 72 caratteri) con blowfish


91

L'ultima settimana ho letto molti articoli sull'hashing delle password e Blowfish sembra essere (uno dei) il miglior algoritmo di hashing in questo momento, ma non è questo l'argomento di questa domanda!

Il limite di 72 caratteri

Blowfish considera solo i primi 72 caratteri nella password inserita:

<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);

$input = substr($password, 0, 72);
var_dump($input);

var_dump(password_verify($input, $hash));
?>

L'output è:

string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)

Come puoi vedere solo i primi 72 caratteri contano. Twitter utilizza blowfish aka bcrypt per memorizzare le proprie password ( https://shouldichangemypassword.com/twitter-hacked.php ) e indovina un po ': cambia la tua password di Twitter con una password lunga con più di 72 caratteri e puoi accedere al tuo account tramite inserendo solo i primi 72 caratteri.

Blowfish e pepe

Ci sono molte opinioni diverse sulle password "infarcite". Alcune persone dicono che non è necessario, perché devi presumere che anche la stringa di pepe segreta sia nota / pubblicata in modo da non migliorare l'hash. Ho un server database separato quindi è del tutto possibile che solo il database sia trapelato e non il pepe costante.

In questo caso (pepe non trapelato) fai un attacco basato su un dizionario più difficile (correggimi se questo non è giusto). Se anche il filo di pepe è fuoriuscito: non è poi così male: hai ancora il sale ed è protetto come un hashish senza pepe.

Quindi penso che infarcire la password non sia almeno una cattiva scelta.

Suggerimento

Il mio suggerimento per ottenere un hash Blowfish per una password con più di 72 caratteri (e pepe) è:

<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";

// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);

// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);

var_dump(password_verify($input_peppered, $hash));
?>

Questo si basa su questa domanda : password_verifyritorno false.

La domanda

Qual è il modo più sicuro? Ottenere prima un hash SHA-256 (che restituisce 64 caratteri) o considerare solo i primi 72 caratteri della password?

Professionisti

  • L'utente non può accedere inserendo solo i primi 72 caratteri
  • Puoi aggiungere il pepe senza superare il limite di caratteri
  • L'output di hash_hmac avrebbe probabilmente più entropia della password stessa
  • La password è sottoposta ad hashing da due diverse funzioni

Contro

  • Solo 64 caratteri vengono utilizzati per costruire l'hash del pesce palla


Modifica 1: questa domanda riguarda solo l'integrazione PHP di blowfish / bcrypt. Grazie per i commenti!


3
Blowfish non è l'unico che tronca la password, fuorviando le persone a pensare che sia più sicuro di quanto non sia in realtà. Ecco una storia interessante del limite di 8 caratteri.
DOK

2
Il troncamento di 72 caratteri è fondamentale per l'algoritmo Blowfish o solo per l'implementazione PHP? IIRC Blowfish viene utilizzato anche su (almeno alcuni) 'nix per crittografare le password degli utenti.
Douglas B. Staple

3
Il problema è con Bcrypt, non con Blowfish. Posso riprodurre questo problema solo con Python e Bcrypt.
Blender

@ Blender: grazie per il tuo commento e il tuo lavoro. Non sono riuscito a trovare funzioni diverse in php per blowfish e bcrypt e sebbene siano le stesse. Ma non fa differenza per me in php? Preferirei usare la funzione php standard.
Frederik Kammer

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

Risposte:


135

Il problema qui è fondamentalmente un problema di entropia. Quindi iniziamo a cercare lì:

Entropia per carattere

Il numero di bit di entropia per byte sono:

  • Caratteri esadecimali
    • Bit: 4
    • Valori: 16
    • Entropia in 72 caratteri: 288 bit
  • Alfanumerico
    • Bit: 6
    • Valori: 62
    • Entropia in 72 caratteri: 432 bit
  • Simboli "comuni"
    • Punte: 6.5
    • Valori: 94
    • Entropia in 72 caratteri: 468 bit
  • Byte completi
    • Bit: 8
    • Valori: 255
    • Entropia in 72 caratteri: 576 bit

Quindi, il modo in cui agiamo dipende dal tipo di personaggi che ci aspettiamo.

Il primo problema

Il primo problema con il codice è che il passaggio dell'hash "pepe" è l'output di caratteri esadecimali (poiché il quarto parametro su hash_hmac()non è impostato).

Pertanto, eseguendo l'hashing del tuo pepe, stai effettivamente riducendo la massima entropia disponibile per la password di un fattore 2 (da 576 a 288 bit possibili ).

Il secondo problema

Tuttavia, sha256fornisce solo 256bit di entropia in primo luogo. Quindi stai effettivamente riducendo un possibile 576 bit fino a 256 bit. Il tuo passaggio di hash * immediatamente *, per definizione, perde almeno il 50% della possibile entropia nella password.

Potresti risolvere parzialmente questo problema passando a SHA512, dove ridurrai l'entropia disponibile solo di circa il 12%. Ma questa è ancora una differenza non insignificante. Quel 12% riduce il numero di permutazioni di un fattore 1.8e19. È un numero elevato ... e questo è il fattore che lo riduce di ...

Il problema sottostante

Il problema di fondo è che esistono tre tipi di password con più di 72 caratteri. L'impatto che questo sistema di stile avrà su di loro sarà molto diverso:

Nota: da qui in poi presumo che ci stiamo confrontando con un sistema pepper che utilizza SHA512con output grezzo (non esadecimale).

  • Password casuali ad alta entropia

    Questi sono i tuoi utenti che utilizzano generatori di password che generano una quantità di chiavi grandi per le password. Sono casuali (generati, non scelti dall'uomo) e hanno un'entropia elevata per personaggio. Questi tipi utilizzano byte elevati (caratteri> 127) e alcuni caratteri di controllo.

    Per questo gruppo, la tua funzione di hashing ridurrà significativamente la loro entropia disponibile in bcrypt.

    Lasciatemelo dire di nuovo. Per gli utenti che utilizzano password lunghe e ad alta entropia, la tua soluzione riduce significativamente la forza della loro password di una quantità misurabile. (62 bit di entropia persi per una password di 72 caratteri e di più per password più lunghe)

  • Password casuali di media entropia

    Questo gruppo utilizza password contenenti simboli comuni, ma non byte alti o caratteri di controllo. Queste sono le tue password digitabili.

    Per questo gruppo, lo farai leggermente sbloccherai più entropia (non la creerai, ma consentirai a più entropia di adattarsi alla password bcrypt). Quando dico leggermente, intendo leggermente. Il pareggio si verifica quando si massimizzano i 512 bit di SHA512. Pertanto, il picco è di 78 caratteri.

    Lasciatemelo dire di nuovo. Per questa classe di password, è possibile memorizzare solo altri 6 caratteri prima di esaurire l'entropia.

  • Password non casuali a bassa entropia

    Questo è il gruppo che utilizza caratteri alfanumerici che probabilmente non sono generati in modo casuale. Qualcosa di simile a una citazione della Bibbia o simile. Queste frasi hanno circa 2,3 bit di entropia per carattere.

    Per questo gruppo, puoi sbloccare in modo significativo più entropia (non crearla, ma consentire a più di entrare nella password di bcrypt) tramite hashing. Il pareggio è di circa 223 caratteri prima che si esaurisca l'entropia.

    Diciamolo di nuovo. Per questa classe di password, il pre-hashing aumenta decisamente la sicurezza in modo significativo.

Ritorno al mondo reale

Questi tipi di calcoli di entropia non hanno molta importanza nel mondo reale. Ciò che conta è indovinare l'entropia. Questo è ciò che influenza direttamente ciò che gli aggressori possono fare. Questo è ciò che vuoi massimizzare.

Sebbene ci siano poche ricerche per indovinare l'entropia, ci sono alcuni punti che vorrei sottolineare.

Le possibilità di indovinare a caso 72 caratteri corretti di seguito sono estremamente basse. Hai più probabilità di vincere alla lotteria Powerball 21 volte, piuttosto che avere questa collisione ... Ecco quanto è grande il numero di cui stiamo parlando.

Ma potremmo non inciampare su di esso statisticamente. Nel caso delle frasi, la probabilità che i primi 72 caratteri siano gli stessi è molto più alta rispetto a una password casuale. Ma è ancora banalmente basso (è più probabile che vincerai la lotteria Powerball 5 volte, sulla base di 2,3 bit per personaggio).

In pratica

In pratica, non importa davvero. Le possibilità che qualcuno indovini correttamente i primi 72 caratteri, dove questi ultimi fanno una differenza significativa, sono così basse che non vale la pena preoccuparsi. Perché?

Bene, diciamo che stai prendendo una frase. Se la persona riesce a interpretare correttamente i primi 72 caratteri, è davvero fortunata (non probabile) o è una frase comune. Se è una frase comune, l'unica variabile è il tempo necessario per realizzarla.

Facciamo un esempio. Prendiamo una citazione dalla Bibbia (solo perché è una fonte comune di testo lungo, non per nessun altro motivo):

Non desiderare la casa del tuo prossimo. Non desiderare la moglie del tuo vicino, il suo servo o la sua schiava, il suo bue o l'asino o qualsiasi cosa che appartenga al tuo prossimo.

Sono 180 caratteri. Il 73 ° personaggio è gil secondo neighbor's. Se hai indovinato così tanto, probabilmente non ti fermerai a nei, ma continuerai con il resto del verso (poiché è così che è probabile che venga utilizzata la password). Pertanto, il tuo "hash" non ha aggiunto molto.

BTW: ASSOLUTAMENTE NON sto sostenendo l'uso di una citazione della Bibbia. In effetti, l'esatto contrario.

Conclusione

Non aiuterai molto le persone che usano password lunghe eseguendo prima l'hashing. Alcuni gruppi puoi sicuramente aiutare. Alcuni possono sicuramente ferire.

Ma alla fine, niente di tutto ciò è eccessivamente significativo. I numeri con cui abbiamo a che fare sono SEMPRE troppo alti. La differenza di entropia non sarà molto.

È meglio lasciare bcrypt così com'è. È più probabile che rovini l'hashing (letteralmente, l'hai già fatto e non sei il primo o l'ultimo a commettere quell'errore) di quanto l'attacco che stai cercando di prevenire accadrà.

Concentrati sulla protezione del resto del sito. E aggiungi un misuratore di entropia della password alla casella della password al momento della registrazione per indicare la forza della password (e indicare se una password è troppo lunga che l'utente potrebbe desiderare di cambiarla) ...

Questo è almeno il mio $ 0,02 (o forse molto più di $ 0,02) ...

Per quanto riguarda l'utilizzo di un pepe "segreto":

Non c'è letteralmente alcuna ricerca sull'alimentazione di una funzione hash in bcrypt. Pertanto, nella migliore delle ipotesi non è chiaro se inserire un hash "pepato" in bcrypt causerà mai vulnerabilità sconosciute (sappiamo che ciò hash1(hash2($value))può esporre vulnerabilità significative intorno alla resistenza alle collisioni e agli attacchi preimage).

Considerando che stai già pensando di conservare una chiave segreta (il "pepe"), perché non usarla in un modo ben studiato e compreso? Perché non crittografare l'hash prima di archiviarlo?

Fondamentalmente, dopo aver hash la password, inserisci l'intero output hash in un algoritmo di crittografia potente. Quindi memorizzare il risultato crittografato.

Ora, un attacco SQL-Injection non farà trapelare nulla di utile, perché non hanno la chiave di cifratura. E se la chiave è trapelata, gli aggressori non stanno meglio che se usassi un semplice hash (che è dimostrabile, qualcosa con il pepe "pre-hash" non fornisce).

Nota: se scegli di farlo, usa una libreria. Per PHP, consiglio vivamente il Zend\Cryptpacchetto di Zend Framework 2 . In realtà è l'unico che consiglierei in questo momento. È stato fortemente rivisto e prende tutte le decisioni per te (il che è molto positivo) ...

Qualcosa di simile a:

use Zend\Crypt\BlockCipher;

public function createHash($password) {
    $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);

    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    return $blockCipher->encrypt($hash);
}

public function verifyHash($password, $hash) {
    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    $hash = $blockCipher->decrypt($hash);

    return password_verify($password, $hash);
}

Ed è vantaggioso perché stai utilizzando tutti gli algoritmi in modi che sono ben compresi e ben studiati (almeno relativamente). Ricorda:

Chiunque, dal dilettante più incapace al miglior crittografo, può creare un algoritmo che lui stesso non può rompere.


6
Grazie mille per questa risposta dettagliata. Questo mi aiuta davvero!
Frederik Kammer

1
Il mio complimento per questa risposta. Un piccolo pignolo però, è la grande maggioranza degli utenti, che usa password molto deboli, parole e derivati ​​contenuti in un dizionario per crackare le password, un peperoncino li proteggerebbe indipendentemente dalle domande entrophy. Per evitare di perdere entrophy, potresti semplicemente concatenare password e pepe. Tuttavia, il tuo suggerimento sulla crittografia del valore hash è probabilmente la soluzione migliore per aggiungere un segreto lato server.
martinstoeckli

2
@martinstoeckli: Il mio problema con il concetto di pepe non è nel valore di esso. È in quanto l'applicazione del "pepe" va in un territorio inesplorato in termini di algoritmi crittografici. Non è una buona cosa. Invece, credo che le primitive crittografiche dovrebbero essere combinate in un modo che sono state progettate per andare insieme. Fondamentalmente, il concetto di base di un pepe mi suona nelle orecchie come alcune persone che non sapevano nulla di crittografia hanno detto "più hash sono meglio! Abbiamo sale, anche il pepe è buono!" . Preferisco semplicemente avere un
impl

@ircmaxell - Sì, conosco il tuo punto di vista e sono d'accordo, purché i valori hash vengano crittografati in seguito. Se non si esegue questo passaggio aggiuntivo, un attacco del dizionario rivelerà semplicemente troppe password deboli, anche con un buon algoritmo di hash.
martinstoeckli

@martinstoeckli: non sono d'accordo su questo. La memorizzazione dei segreti non è una cosa banale da fare. Invece, se usi bcrypt con un buon costo (12 oggi), tutte le password tranne la classe più debole sono sicure (il dizionario e le password banali sono quelle deboli). Quindi preferirei raccomandare alle persone di concentrarsi sull'educazione dell'utente con misuratori di forza e convincerli a utilizzare password migliori in primo luogo ...
ircmaxell

5

Infarcire le password è sicuramente una buona cosa da fare, ma vediamo perché.

Per prima cosa dovremmo rispondere alla domanda quando esattamente un peperone aiuta. Il pepe protegge solo le password, fintanto che rimane segreta, quindi se un utente malintenzionato ha accesso al server stesso, non serve. Un attacco molto più semplice è tuttavia SQL-injection, che consente l'accesso in lettura al database (ai nostri valori hash), ho preparato una demo di SQL-injection per mostrare quanto può essere facile (fai clic sulla freccia successiva per ottenere un ingresso).

Allora cosa aiuta effettivamente il pepe? Finché il pepe rimane segreto, protegge le password deboli da un attacco del dizionario. La password 1234diventerebbe quindi qualcosa di simile 1234-p*deDIUZeRweretWy+.O. Questa password non solo è molto più lunga, contiene anche caratteri speciali e non farà mai parte di nessun dizionario.

Ora possiamo stimare quali password useranno i nostri utenti, probabilmente più utenti inseriranno password deboli, poiché ci sono utenti con password tra 64-72 caratteri (in realtà questo sarà molto raro).

Un altro punto è l'intervallo di forzatura bruta. La funzione hash sha256 restituirà un output di 256 bit o combinazioni 1.2E77, che è troppo per il brute-forcing, anche per le GPU (se calcolato correttamente, ciò richiederebbe circa 2E61 anni su una GPU nel 2013). Quindi non otteniamo un vero svantaggio applicando il pepe. Poiché i valori hash non sono sistematici, non è possibile accelerare la forzatura bruta con schemi comuni.

PS Per quanto ne so, il limite di 72 caratteri è specifico per l'algoritmo di BCrypt stesso. La migliore risposta che ho trovato è questa .

PPS Penso che il tuo esempio sia difettoso, non puoi generare l'hash con l'intera lunghezza della password e verificarlo con uno troncato. Probabilmente intendevi applicare il pepe allo stesso modo per la generazione dell'hash e per la verifica dell'hash.


Per quanto riguarda il tuo PPS, posso solo dire: Sì, può verificare la password troncata con l'hash di quella non troncata e ottenere comunque true. Ecco di cosa tratta questa domanda. Dai
Sliq

@Panique - Il problema non è il calcolo dell'hash BCrypt, è l'HMAC prima. Per generare l'hash SHA, l'OP utilizza la password a lunghezza intera e utilizza il risultato come input per BCrypt. Per la verifica, tronca la password prima di calcolare l'hash SHA, quindi utilizza questo risultato completamente diverso come input per BCrypt. L'HMAC accetta input di qualsiasi lunghezza.
martinstoeckli

2

Bcrypt utilizza un algoritmo basato sul costoso algoritmo di configurazione della chiave Blowfish.

Il limite di password di 56 byte consigliato (incluso il byte di terminazione nullo) per bcrypt si riferisce al limite di 448 bit della chiave Blowfish. Qualsiasi byte oltre tale limite non viene completamente miscelato nell'hash risultante. Il limite assoluto di 72 byte sulle password bcrypt è quindi meno rilevante, se si considera l'effetto effettivo sull'hash risultante di quei byte.

Se pensi che i tuoi utenti normalmente sceglierebbero password di lunghezza superiore a 55 byte, ricorda che puoi sempre aumentare invece i giri di estensione della password, per aumentare la sicurezza in caso di violazione della tabella delle password (anche se questo deve essere molto rispetto all'aggiunta di extra personaggi). Se i diritti di accesso degli utenti sono così critici che gli utenti normalmente richiederebbero una password enormemente lunga, anche la scadenza della password dovrebbe essere breve, come 2 settimane. Ciò significa che è molto meno probabile che una password rimanga valida mentre un hacker investe le proprie risorse per sconfiggere il fattore di lavoro coinvolto nel testare ogni password di prova per vedere se produrrà un hash corrispondente.

Ovviamente, nel caso in cui la tabella delle password non venga violata, dovremmo consentire agli hacker, al massimo, dieci tentativi di indovinare la password di 55 byte di un utente, prima di bloccare l'account dell'utente;)

Se decidi di pre-hash una password che è più lunga di 55 byte, dovresti usare SHA-384, poiché ha l'output più grande senza superare il limite.


1
"anche la scadenza della password dovrebbe essere breve, come 2 settimane" di "password massicciamente lunghe", davvero, perché preoccuparsi anche di salvare la password, basta usare la reimpostazione della password ogni volta. Seriamente, questa è la soluzione sbagliata, passa all'autenticazione a due fattori con un token.
Zaf

Grazie @zaph. Sei in grado di indicarmi un esempio di questo? Sembra interessante.
Phil

[DRAFT NIST Special Publication 800-63B Digital Authentication Guideline] ( pages.nist.gov/800-63-3/sp800-63b.html ), 5.1.1.2. Verificatori segreti memorizzati : i verificatori NON DOVREBBERO richiedere che i segreti memorizzati vengano modificati arbitrariamente (ad esempio, periodicamente) . Vedi anche Toward Better Password Requirements di Jim Fenton.
Zaph

1
Il fatto è che più spesso a un utente viene richiesto di cambiare una password, peggiore diventa la scelta della password, riducendo così la sicurezza. L'utente ha una quantità limitata di buone password memorizzabili e si esauriscono, scegliendo password davvero pessime o scrivendole su post-it attaccati alla parte inferiore della tastiera, ecc.
Zaph
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.