it-swarm-eu.dev

Perché improvvisare la tua funzione Hash dalle funzioni hash esistenti è così male

Temo che mi verranno lanciati dei pomodori per aver fatto questa vecchia domanda, ma qui va.

Dopo aver letto che cucinare il proprio hash della password dalle funzioni di hashing esistenti è pericoloso over e over di nuovo non capisco ancora la logica. Ecco alcuni esempi:

  • md5(md5(salt) + bcrypt(password))
  • scrypt(bcrypt(password + salt))
  • sha1(md5(scrypt(password + md5(salt))))

Gli argomenti tipici contro questi vanno come segue:

Non sei un crittografo! Non hai idea se questi hash sono più sicuri. Lascialo agli esperti che sanno cosa stanno facendo. Questi non aggiungono ulteriore sicurezza.

Certo, non migliorano la funzione come hash (cioè rendono più difficile invertire o trovare collisioni ecc.), Ma sicuramente sicuramente non ce la fanno peggio come hash? Se lo facessero, gli hacker sarebbero in grado di ricodificare le password con hash standard in questi hash stravaganti quando lo ritengono opportuno e indeboliscono l'hash? Non lo compro.

Secondo argomento:

Principio di Kerckoffs : Un sistema crittografico dovrebbe essere sicuro anche se tutto ciò che riguarda il sistema è noto.

Concordato. Questa è fondamentalmente la motivazione per non memorizzare le tue password come testo in chiaro in primo luogo. Ma se la mia risposta alla prima critica è valida, questi hash stravaganti funzionano ancora come hash sicuri e il nostro sistema non infrange il principio di Kerckoffs più di quanto non farebbe con un hash standard.

Qui ci sono due possibili (e vale la pena, per quanto posso vedere) vantaggi nell'usare un hash "stravagante" rispetto a un hash normale:

  1. Certo, il tuo sistema dovrebbe essere sicuro se l'attaccante ha il codice sorgente, ma è molto probabile che l'attaccante non accederà al tuo codice sorgente e probabilmente non sarà in grado di indovinare il tuo stravagante hash, rendendo impossibile qualsiasi tentativo di una forza bruta.
  2. (Questa è la vera motivazione dietro a me che faccio questa domanda) BCrypt è pensato per essere sicuro, difficile per CPU e GPU (ottimo) ma può essere molto veloce con hardware specializzato . Si dice che SCrypt sia difficile da potenziare su CPU, GPU e attualmente disponibile specializzato specializzato, ma è più recente e non fidato dalla comunità crittografica tanto quanto BCrypt a causa della mancanza di esposizione che ha avuto. Ma l'hash BCrypt(SCrypt(password + salt)) non ottiene il meglio da entrambi i mondi?

Mi rendo conto che la passione/rabbia dietro la maggior parte degli inganni contro questi hash prodotti in casa proviene dalla mancanza di conoscenza da parte del programmatore medio di ciò che rende un buon hash, e la preoccupazione che incoraggiare questo tipo di hash stravagante finirà inevitabilmente con debole e inutile hash che entrano nel codice di produzione. Ma Se l'hash stravagante è costruito con cura da hash solidi e affidabili, i guadagni in termini di sicurezza non sono molto preziosi e reali?


Aggiornare

Ho avuto un sacco di buone risposte su questo, grazie. Quello che mi sembrava di trascurare nelle mie assunzioni era che, sebbene la combinazione di hash non possa rendere più facile decifrare la password originale e quindi decifrare gli hash costituenti, la combinazione di due o più hash sicuri può - almeno in linea di principio - essere più debole di uno qualsiasi dei suoi hash interni a causa delle interazioni non studiate e complesse tra loro. Significa che potrebbe essere possibile trovare some stringa che ha superato l'hash stravagante senza necessariamente rompere gli hash che lo hanno inventato.

80
George Powell

Il fatto che devi porre questa domanda è la risposta stessa: tu non sai cosa c'è di sbagliato nel raggruppare queste primitive, e quindi impossibile sapere quali sono i vantaggi o i punti deboli.

Facciamo alcune analisi su ciascuno degli esempi che hai dato:

md5(md5(salt) + bcrypt(password))

Posso vedere alcuni problemi qui. Il primo è che stai preparando il sale. Che vantaggio offre? Nessuna. Aggiunge complessità e il sale è semplicemente pensato per essere unico per prevenire collisioni con password e attacchi di pre-calcolo (ad esempio la tabella Rainbow). L'uso di MD5 qui non ha alcun senso e potrebbe effettivamente indebolire lo schema poiché MD5 ha conosciuto banali collisioni. Come tale, c'è una piccola possibilità che l'introduzione di MD5 qui potrebbe significare che due sali unici producono lo stesso hash MD5, risultando in un sale effettivamente duplicato. Questo è male.

Successivamente, si utilizza bcrypt sulla password. Ok. Bene, la maggior parte delle implementazioni di bcrypt richiedono un sale internamente, quindi questo è già tecnicamente non valido. Supponiamo che tu lo sappia e intendevi dire bcrypt(md5(salt), password). Questa parte sta ancora cadendo nella debolezza che ho descritto sopra, ma non è troppo malandata: rimuovi MD5 ed è un uso standard di bcrypt.

Infine, MD5 è tutto. Perché stai facendo questo? Qual è lo scopo? Che beneficio porta? Per quanto posso vedere, non vi è alcun vantaggio. Dal punto di vista del pregiudizio, aggiunge più complessità. Poiché la maggior parte delle implementazioni di bcrypt usano la notazione $2a$rounds$salt$hash, Dovrai scrivere il codice per analizzarlo in modo da poter estrarre la parte hash e archiviare il resto separatamente. Avrai anche bisogno di un'implementazione MD5, che non era necessaria.

Quindi, in termini di footprint di codice per potenziali vettori di attacco, sei passato da una semplice implementazione di bcrypt a un'implementazione di bcrypt con codice di analisi personalizzato e implementazione di MD5 e un po 'di codice di colla per incollare tutto insieme. Per zero benefici e una potenziale vulnerabilità nella gestione del sale.

Successiva:

scrypt(bcrypt(password + salt))

Questo non è poi così male, ma di nuovo hai bisogno di un po 'di codice per analizzare i risultati di bcrypt in hash e salt/round count separatamente. In questo caso suppongo che ci sia un leggero vantaggio, perché bcrypt e scrypt funzionano in modi diversi per approssimativamente lo stesso obiettivo, che renderebbe un po 'più difficile per un attaccante estremamente ben finanziato costruire ASIC personalizzati per infrangere il tuo schema. Ma è davvero necessario ? davvero colpirai una situazione in cui uno stato nazionale dedicherà qualche milione di dollari solo per rompere il tuo hashish? E, in tal caso, sarà davvero fastidioso per l'attaccante dover spendere qualche milione in più per raddoppiare il numero di chip?

Un altro potenziale problema con la combinazione di bcrypt e scrypt in questo modo è che sono stati fatti pochissimi studi su come interagiscono i due. Pertanto, noi non sappiamo se ci sono casi strani che possono causare problemi. Come esempio più ovvio, prendi il time pad. Calcoliamo c=m^k Per alcuni messaggi m e alcuni tasti ugualmente perfettamente casuali k e otteniamo una sicurezza perfetta. Quindi facciamolo due volte , per una sicurezza ancora maggiore! Questo ci dà c=m^k^k ... oh, aspetta, ci dà solo m. Quindi, poiché non ci siamo presi il tempo per capire correttamente come funzionavano gli interni del sistema, siamo finiti con una vera vulnerabilità di sicurezza. Ovviamente è più complicato nel caso dei KDF, ma si applica lo stesso principio.

E infine:

sha1(md5(scrypt(password + md5(salt))))

Ancora una volta ci imbattiamo nel problema del sale dell'MD5. Sono anche incuriosito da MD5 nell'hash SHA1. Quale possibile vantaggio potrebbe avere, se stai già utilizzando un KDF lento come Scrypt? I pochi nanosecondi necessari per calcolare quegli hash palles in confronto alle centinaia di millisecondi necessari per calcolare il digest scrypt della password. Stai aggiungendo complessità per un livello assolutamente irrilevante di "sicurezza", che è sempre una brutta cosa. Ogni riga di codice che scrivi è una potenziale vulnerabilità.


Ora ricorda quel punto che ho sollevato all'inizio della mia risposta. Se, in qualsiasi momento di questa risposta, hai pensato "oh sì, non ci ho pensato", allora il mio punto è dimostrato.

Stai incontrando quello che definirei come --- la falsa massima di Dave :

Se aggiungo più cose crittografiche, sarà più sicuro.

Questo è un tratto comune tra gli sviluppatori, e una volta ci ho creduto anche io. Va di pari passo con la negazione di altri principi, come Principio di Kerckhoff . In definitiva, devi capire e accettare che l'oscurità non è una barriera di sicurezza; è una stampella per criptovaluta debole. Se la tua criptovaluta è forte, non ha bisogno di stampella.

70
Polynomial

Le primitive crittografiche possono essere impilate in modo sicuro e aumentare la sicurezza se, e solo se, conosci le primitive abbastanza bene da capire le loro debolezze e il modo in cui tali debolezze interagiscono. Se non li conosci o non capisci i dettagli, beh, ecco come ottieni protocollo di Dave .

Il problema è che pochissime persone le conoscono abbastanza bene per giudicare se una certa combinazione è sicura. Ecco perché deve essere qualcosa che viene pubblicato e rivisto, se non è stato rivisto non hai modo di sapere se è forte come scrypt o se è più vicino a CRC32.

Quindi, se non sei un esperto, è possibile che tu abbia qualcosa di più debole del primitivo più debole che hai usato (vedi il protocollo di Dave) e non lo sapresti. O almeno non lo sapresti fino a quando non viene crackato: trovare le password dei tuoi utenti su Pastebin non è proprio il modo ideale per determinare che lo schema è difettoso.

Sono d'accordo sul fatto che un certo grado di oscurità può aiutare da una prospettiva di difesa approfondita, ma il sistema sottostante deve essere sicuro.

Tra scrypt, bcrypt e PBDKF2 - almeno uno di questi sarà supportato praticamente su ogni piattaforma. Questi sono noti e ben collaudati: offrono diversi livelli di protezione, ma sono ancora molto più sicuri di un bizzarro stacking di md5 e sha1.

15
Adam Caudill

Per la tua specifica domanda sulla combinazione di scrypt e bcrypt, ricorda che queste funzioni hanno un costo configurabile e desideri aumentare tale costo il più possibile, mantenendolo tollerabile per il tuo specifico utilizzo. Ad esempio, se puoi usare bcrypt con fino a [~ # ~] x [~ # ~] iterazioni (oltre le quali è troppo costoso per il tuo server e il numero medio di connessioni utente al secondo) o scrypt con fino a [~ # ~] y [~ # ~] iterazioni, quindi tu impossibile usare scrypt (bcrypt) con [~ # ~] x [~ # ~] iterazioni per bcrypt quindi [~ # ~] y [~ # ~] iterazioni per scrypt: questo andrà oltre il budget della tua CPU.

Quindi, se scrypt e bcrypt a cascata, devi usare entrambi con meno iterazioni rispetto a quello che avresti potuto fare con uno solo. Non "ottieni il meglio da entrambi i mondi" semplicemente mettendoli insieme. In effetti, il meglio che puoi sperare è una specie di media tra i due. E questo ha un prezzo di codice più complesso, qualcosa che è intrinsecamente negativo quando si parla di sicurezza (o, per questo, di manutenibilità).

13
Thomas Pornin

Oltre alla risposta di Adam, vorrei anche ricordare che ogni volta che usi la crittografia, dovresti avere una ragione forte e inevitabile per farlo. Nei tuoi esempi sopra, questo non esiste.

md5(md5(salt) + bcrypt(password))
scrypt(bcrypt(password + salt))

Gli algoritmi bcrypt e scrypt sono già abbastanza forti e considerati effettivamente indistruttibili. Che problema stai cercando di risolvere? E perché credi che combinando i loro risultati (in particolare con md5) lo risolverà? Nel migliore dei casi, probabilmente hai semplicemente ridotto la difficoltà di decifrare la password a quella dell'hash più debole, piuttosto che migliorare effettivamente la sicurezza. E lo scenario peggiore è spaventosamente indefinito.

md5(sha1(md5(md5(password) + sha1(password + salt)) + password))

Questa soluzione è anche peggio. Implementa manualmente uno schema di hashing ripetuto, ma senza abbastanza round per imporre effettivamente un fattore di lavoro significativo agli attaccanti.

In poche parole, il problema è che:

  • stai lanciando intorno alla crittografia senza effettivamente avere un problema che deve essere risolto
  • hai aumentato notevolmente la probabilità di introdurre difetti nella tua implementazione
  • probabilmente hai ridotto la sicurezza al più debole degli algoritmi di hashing e
  • hai introdotto uno scenario sconosciuto nel caso peggiore in cui non esisteva nessuno
9
Stephen Touset

Se si applicano operazioni non sicure a un algoritmo sicuro, è possibile interrompere definitivamente la funzione di hashing. La tua nuova funzione potrebbe persino essere molto peggio del collegamento più debole.

Perché gli attaccanti non usano questo per interrompere le funzioni sicure? Non li aiuta. Ad esempio, se sovrascrivo i primi 440 bit di una password archiviata in modo sicuro utilizzando bcrypt con zero, posso facilmente trovare una password corrispondente con la forza bruta, ma quella password funzionerà solo sul mio terribile algoritmo. Un'implementazione sana probabilmente la respingerebbe.

L'azzeramento di grossi pezzi di un hash è chiaramente negativo, ma anche operazioni sicure possono essere combinate in qualcosa di pericoloso. L'aggiunta di due numeri (modulo n per garantire una lunghezza costante) è "sicura". In generale, nessuna entropia viene persa. Tuttavia, h (x) + h(x) mod n riduce la qualità dell'hash h (x) di un bit, poiché il risultato è sempre uniforme. L'operatore altrettanto sicuro XOR fa anche peggio, poiché h (x) XOR h (x) = restituisce sempre zero.

Queste insidie ​​sono abbastanza ovvie, ma non tutte. Tieni presente che, come sempre, è banale inventare uno schema abbastanza buono da non trovare punti deboli da solo, ma molto difficile inventarne uno dove nessun altro può farlo.

8
Marcks Thomas

Le funzioni hash sono costruite dai crittografi e distrutte dai crittografi. Ci sono molte funzioni di hash forte e deboli che vengono ancora utilizzate oggi. I programmatori dovrebbero fidarsi dei crittografi e della funzione hash. Se ci fosse mai stata una vulnerabilità nella funzione hash, allora ne avresti sicuramente sentito parlare su Internet o attraverso i colleghi e quindi i crittografi sicuramente farebbero un'indagine approfondita. Con qualsiasi algoritmo di hash sicuro, è noto solo che la debolezza può essere un attacco bruteforce.

La combinazione di funzioni hash non aggiunge quasi nessuna sicurezza aggiuntiva e qualsiasi cosa tu possa voler aggiungere, probabilmente è già già implementata nella funzione.

Salare una password è ottimo per ridurre l'efficacia contro le tabelle Rainbow in modo tale che una password non possa essere semplicemente "cercata". Che tu abbia una funzione di hash due volte o cambi la funzione di hash, sta essenzialmente salando la password. E la maggior parte delle funzioni include un metodo semplice per salare, quindi non è davvero necessario implementarlo.

Diciamo che voglio creare il mio hash sicuro perché lo fanno tutti. E poiché non sono un crittografo, ne avrò bisogno "davvero" sicuro, perché ovviamente ogni programmatore sa come fare un hash sicuro invece di usare quelli sicuri già creati. Quindi creo la mia subdola funzione hash, mod10 (md5 (sha1 (bcrypt (password + salt)))).

Come puoi vedere dalla mia funzione hash, è davvero sicuro perché ci uso così tante cose diverse. Naturalmente in questo stupido esempio, è facile vedere che ci saranno solo 10 diverse possibili uscite. Ma semplicemente usando una singola funzione di hash sicura, lo avrebbe completamente evitato.

Certo, il tuo sistema dovrebbe essere sicuro se l'attaccante ha il codice sorgente, ma è molto probabile che l'attaccante non abbia accesso al tuo codice sorgente e probabilmente non sarà in grado di indovinare il tuo stravagante hash, facendo qualsiasi tentativo di un bruto forza impossibile

Quindi supponiamo che un attaccante abbia acquisito la tabella del database che contiene gli hash. Suppongo che sarebbe molto probabile che un utente malintenzionato possa ottenere anche i file della pagina Web. I tuoi sistemi che eseguono questi servizi potrebbero essere lo stesso exploit che ti ha permesso di prendere il database. Un server di database è impostato in modo tale che il pubblico non possa accedervi direttamente. D'altra parte, il tuo server web contenente il tuo codice è in prima linea.

6
ponsfonze

Ogni volta che aumenti la complessità di un algoritmo o addirittura aggiungi più righe di codice, aumenti i punti di errore nella tua applicazione. Potrebbero esserci conseguenze indesiderate della combinazione di algoritmi. Ciò può portare a determinati indica o altri segni che possono effettivamente indebolire la forza crittografica del sistema.

Più librerie usi nella tua applicazione, maggiore è il rischio per la tua applicazione in generale. Se si riscontra un difetto in un'implementazione che consente una debolezza, l'esecuzione di codice, ecc., L'applicazione è vulnerabile. Se per caso ti è capitato di selezionare un altro algoritmo che non è stato attaccato, per il momento sei al sicuro (ovviamente potresti anche essere dalla parte sfortunata della fortuna).

Ricorda [~ # ~] kiss [~ # ~] : rendilo semplice, stupido , altrimenti potresti perderti nella complessità.

5
Eric G

Non sarò d'accordo con un sacco di persone che sono più intelligenti di me e più esperte di sicurezza di me. Quindi probabilmente mi sbaglio.

Improvvisare la tua funzione hash è una buona idea, se lo fai nel modo giusto. Segui questi 3 semplici passaggi.

  1. Identificare una debolezza o un difetto nelle funzioni hash esistenti.
  2. Improvvisa la tua funzione di hash che non ha questo difetto.
  3. Verifica che la tua funzione hash improvvisata abbia tutti i punti di forza delle funzioni hash esistenti.

Dopo aver completato il passaggio 3, saresti sciocco a non usare la tua funzione hash improvvisata.

2
emory

Quando si concatenano diverse funzioni di hash perché si è preoccupati per quello, è fondamentale applicare il salt back prima di applicare il successivo algoritmo di hashing, quindi eventuali punti deboli di collisione in uno degli algoritmi non contaminano la seconda funzione di hash che si sta utilizzando:

scrypt (bcrypt (password + salt) + salt)

Ma penso che ora scrypt sia una tecnica consolidata, Argon 2i ha vinto la competizione di hashing delle password e si ritiene che sia più sicuro e bcrypt è in circolazione da molto tempo e ha dimostrato di essere sicuro contro banali bypass.

Pertanto, ciò che segue avrebbe più senso e unirebbe la forza dell'argon 2i, ma tornerebbe alla crittografia se un futuro attaccante mostrasse come rompere banalmente l'argon 2i, che attualmente è ritenuto difficile:

bcrypt (Argon2i (password + salt) + salt)

Ma hai meno probabilità di commettere un errore se fai semplicemente:

scrypt (password + salt) O bcrypt (password + salt)

Ricorda, la maggior parte delle violazioni sono dovute a errori umani nel codice, molto meglio per mantenerlo semplice e rivedere a fondo il tuo codice con analisi dinamiche, statiche e revisori del codice umano, per assicurarti che non ci siano attacchi di iniezione sql (Ricorda sempre di parametrizzare le query del database !)

0
user3083447