Perché improvvisare la propria funzione di hash con le funzioni di hash esistenti è così brutto

73

Ho paura che mi vengano lanciati pomodori per aver fatto questa vecchia domanda, ma qui va.

Dopo aver letto che la cottura del proprio hash delle password delle funzioni di hashing esistenti è pericolosa oltre e oltre di nuovo non lo faccio ancora capisco 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 sono i seguenti:

You're not a cryptographer! You've got no idea if these hashes are more secure. Leave it to the experts who know what they're doing. These add no extra security.

Certo che non migliorano la funzione come un hash (cioè rendono più difficile invertire o trovare collisioni ecc.), ma sicuramente sicuramente non fanno peggio come hash? Se lo facessero allora gli hacker sarebbero in grado di ri-hash le password con hash standard in questi wacky hash come meglio credono e indeboliranno l'hash? Non lo compro.

Secondo argomento:

Kerckoffs's principle: A cryptosystem should be secure even if everything about the system is known.

D'accordo. Questa è fondamentalmente la motivazione per non archiviare le password come testo in chiaro in primo luogo. Ma se la mia risposta alla prima critica rimane allora questi wacky hash funzionano ancora come hash sicuri, e il nostro sistema non infrange il principio di Kerckoffs più di quanto farebbe con un hash standard.

Ecco due vantaggi (e vale la pena, a mio avviso) di usare un hash "strambo" su un normale hash:

  1. Certo, il tuo sistema dovrebbe essere sicuro se l'autore dell'attacco ha il codice sorgente, ma è molto probabile che il tuo aggressore non abbia accesso al tuo codice sorgente e probabilmente non sarà in grado di indovinare il tuo stravagante hash, rendendo impossibile qualsiasi tentativo di forza bruta.
  2. (Questa è la vera motivazione dietro di me a porre 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 forzare su CPU, GPU e attualmente disponibile specializzato, ma è più recente e non è considerato attendibile dalla comunità crittografica tanto quanto BCrypt a causa della mancanza di visibilità che ha avuto. Ma l'hash BCrypt(SCrypt(password + salt)) non ottiene il meglio da entrambi i mondi?

Apprezzo che la passione / rabbia dietro la maggior parte delle invenzioni contro questi hash fatti in casa provenga dalla scarsa conoscenza del programmatore medio di ciò che rende un buon hash, e una preoccupazione che incoraggiando questo tipo di hashish inevitabilmente finirà con gli hash deboli e inutili entrano nel codice di produzione. Ma Se il wacky hash è accuratamente costruito con hash solidi e affidabili, i guadagni in sicurezza non sono molto preziosi e reali?

Aggiornamento

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

    
posta George Powell 01.04.2013 - 03:24
fonte

10 risposte

66

Il fatto che tu abbia bisogno di porre questa domanda è la risposta stessa - tu non sai cosa c'è di sbagliato nello stacking di queste primitive, e quindi non puoi possibilmente sapere quali benefici o debolezze ci sono.

Facciamo qualche analisi su ciascuno degli esempi che hai fornito:

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

Posso vedere alcuni problemi qui. Il primo è che stai facendo il MD5. Che beneficio dà questo? Nessuna. Aggiunge complessità e il sale deve semplicemente essere unico per prevenire attacchi di password e precalcoli (ad es. L'uso di MD5 qui non ha alcun senso e potrebbe effettivamente indebolire lo schema poiché MD5 ha conosciuto collisioni banali. Come tale, vi è 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. È male.

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

Infine, MD5 tutto il resto. Perché stai facendo questo? Qual è lo scopo? Che beneficio porta? Per quanto posso vedere, non c'è alcun vantaggio. Dal lato negativo, aggiunge più complessità. Poiché la maggior parte delle implementazioni di bcrypt utilizzano 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 ingombro del codice per i potenziali vettori di attacco, sei passato da una semplice implementazione di bcrypt a un'implementazione di bcrypt con codice di analisi personalizzato e implementazione MD5, e un po 'di codice di colla per attaccarlo tutto insieme. Per il vantaggio zero e una potenziale vulnerabilità nella gestione del sale.

Il prossimo:

scrypt(bcrypt(password + salt))

Questo non è male, ma di nuovo hai bisogno di un codice per analizzare i risultati di bcrypt in hash e conteggio di sale / round separatamente. In questo caso, indovina che c'è un piccolo vantaggio, perché bcrypt e scrypt funzionano in modo diverso per approssimativamente lo stesso obiettivo, il che renderebbe un po 'più difficile per un aggressore estremamente ben finanziato per creare ASIC personalizzati per rompere il tuo schema. Ma è veramente necessario? Sarai veramente andando a colpire una situazione in cui uno stato nazione dedicherà qualche milione di dollari solo per rompere il tuo hash? E, se questo caso dovesse mai sorgere, si preoccuperebbe davvero che l'attaccante debba spendere qualche milione in più per raddoppiare il proprio numero di chip?

Un altro potenziale problema con la combinazione di bcrypt e scrypt come questo è che ci sono stati pochissimi studi su come i due interagiscono. In quanto tale, 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 egualmente lunghi perfettamente casuali chiave k , e otteniamo una sicurezza perfetta. 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, ci siamo ritrovati con una vera vulnerabilità alla sicurezza. Ovviamente è più complicato nel caso dei KDF, ma vale lo stesso principio.

E infine:

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

Di nuovo ci stiamo imbattendo nel problema del sale MD5. Sono anche incuriosito da MD5'ing l'hash SHA1. Quale vantaggio potrebbe avere, se stai già utilizzando un KDF lento come scrypt? I pochi nanosecondi che occorrerebbero per calcolare quegli hashs non erano sufficienti in confronto alle centinaia di millisecondi necessari per calcolare il digest scrypt della password. Stai aggiungendo complessità per uno strato di "sicurezza" assolutamente irrilevante, che è sempre una cosa negativa. Ogni riga di codice che scrivi è una potenziale vulnerabilità.

Ora ricorda quel punto che ho fatto all'inizio della mia risposta. Se, in qualsiasi punto di questa risposta, pensavi "oh sì, non ci pensavo", allora il mio punto è provato.

Ti imbatti in ciò che descriverò come Dave La falsa massima di :

If I add more crypto things, it will be more secure.

Questo è un tratto comune tra gli sviluppatori, e anch'io una volta ci credevo. Va di pari passo con la negazione di altri principi, come Principio di Kerckhoff . In definitiva, devi comprendere e accettare che l'oscurità non è una barriera di sicurezza; è una stampella per cripto debole. Se la tua cripto è strong, non ha bisogno di stampelle.

    
risposta data 01.04.2013 - 13:32
fonte
14

Le primitive di Crypto possono essere impilate in modo sicuro e aumentare la sicurezza se, e solo se, conoscete le primitive abbastanza bene da comprendere le loro debolezze e come queste debolezze interagiscono. Se non li conosci, o non capisci i dettagli - beh, è così che ottieni il protocollo di Dave .

Il problema è che poche persone li conoscono abbastanza bene da giudicare se una determinata combinazione è sicura. È per questo che deve essere qualcosa che viene pubblicato e revisionato, se non è stato revisionato non hai modo di sapere se è strong come scrypt o se è più vicino a CRC32.

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

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

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

    
risposta data 01.04.2013 - 04:03
fonte
12

Per la tua specifica domanda di combinazione di scrypt e bcrypt, ricorda che queste funzioni hanno un costo configurabile e tu vuoi aumentare il più possibile il costo, mantenendolo tollerabile per il tuo uso specifico. Ad esempio, se puoi usare bcrypt con iterazioni fino a X (oltre le quali è troppo costoso per il tuo server e il tuo numero medio di connessioni utente al secondo), o scrypt con fino a Y iterazioni, quindi non puoi usare scrypt (bcrypt) con X iterazioni per bcrypt quindi Y iterazioni per scrypt: questo sarà oltre il tuo budget della CPU.

Pertanto, se si esegue la copia in cascata di scrypt e bcrypt, è necessario utilizzare entrambi con meno iterazioni rispetto a ciò che si sarebbe potuto fare con uno solo. Non "ottieni il meglio da entrambi i mondi" semplicemente stringendoli insieme. In effetti, il meglio che puoi sperare è una specie di media tra i due. E ciò comporta il prezzo di un codice più complesso, qualcosa che è intrinsecamente negativo quando si parla di sicurezza (o, per altro, di manutenibilità).

    
risposta data 01.04.2013 - 19:58
fonte
9

Oltre alla risposta di Adam, vorrei anche menzionare che ogni volta che usi la crittografia, dovresti avere una ragione strong 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 unire i loro risultati (in particolare con md5 ) lo risolverà? Nel migliore dei casi, è probabile che tu abbia semplicemente ridotto la difficoltà di crackare la password a quella dell'hash più debole, invece di 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 un fattore di lavoro significativo agli aggressori.

In poche parole, il problema è che:

  • stai gettando in giro la crittografia senza effettivamente avere un problema che deve essere risolto
  • hai notevolmente aumentato la probabilità di introdurre difetti nella tua implementazione
  • hai probabilmente ridotto la sicurezza al più debole degli algoritmi di hashing e
  • hai introdotto uno scenario peggiore sconosciuto in cui non esisteva nessuno
risposta data 01.04.2013 - 05:10
fonte
7

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 peggiore del link più debole.

Perché gli hacker non usano questo per rompere le funzioni sicure? Non li aiuta. Ad esempio, se sovrascrivo i primi 440 bit di una password memorizzata in modo sicuro utilizzando bcrypt con gli zeri, 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 le operazioni sicure possono essere combinate in qualcosa di pericoloso. L'aggiunta di due numeri (modulo n per garantire una lunghezza costante) è "sicura". Generalmente, nessuna entropia è persa. Ancora, 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 lo sono. Tieni presente che, come sempre, è banale inventare uno schema abbastanza buono da non trovare te stesso nessun punto debole, ma è molto difficile inventarne uno in cui nessun altro può farlo.

    
risposta data 01.04.2013 - 12:54
fonte
6

Le funzioni di hash sono costruite da crittografi e distrutte dai crittografi. Ci sono molte funzioni di hash strong e quelle deboli ancora in uso oggi. I programmatori dovrebbero fidarsi dei crittografi e della funzione hash. Se ci fosse mai una vulnerabilità nella funzione di hash, sicuramente ne avresti sentito parlare su internet o attraverso i colleghi di lavoro e quindi i crittografi faranno sicuramente una profonda indagine. 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 voglia aggiungere, probabilmente è già già implementata nella funzione.

La conversione di una password è ottima per ridurre l'efficacia rispetto alle tabelle arcobaleno in modo tale che una password non possa essere semplicemente "cercata". Che tu abbia cancellato una funzione due volte o modificato la funzione hash, è essenzialmente salendo la password. E la maggior parte delle funzioni include un metodo semplice da utilizzare, quindi non è necessario implementarlo.

Diciamo che voglio creare il mio hash sicuro perché tutti lo fanno. E siccome 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. Così creo la mia subdola funzione di hash, mod10 (md5 (sha1 (bcrypt (password + sale)))).

Come puoi vedere dalla mia funzione di hash, è molto sicuro perché uso così tante cose diverse in esso. Ovviamente in questo stupido esempio, è facile vedere che ci saranno solo 10 diversi output possibili. Ma usando semplicemente una singola funzione di hash sicura, l'avrebbe completamente evitato.

Sure, your system should be secure if the attacker has the source code, but it's a very likely possibility that your attacker wont have access to your source code and probably won't be able to guess your wacky hash, making any attempt at a brute force impossible

Quindi stiamo assumendo che un utente malintenzionato sia entrato nella 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 consentiva di sfruttare il database. Un server di database è configurato in modo tale che il pubblico non possa accedervi direttamente. D'altra parte, il tuo server web che contiene il tuo codice è in prima linea.

    
risposta data 01.04.2013 - 09:36
fonte
5

Ogni volta che aumenti la complessità di un algoritmo o aggiungi anche più righe di codice, aumenti i punti di errore nell'applicazione. Potrebbero esserci conseguenze non intenzionali della combinazione di algoritmi. Ciò può portare a certi segnali o altri segni che possono effettivamente indebolire la forza crittografica del sistema.

Più librerie utilizzate nell'applicazione, maggiore è il rischio per l'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 è successo di selezionare un altro algoritmo che non è stato attaccato, la tua sicurezza per il momento (ovviamente potresti anche essere sfortunato).

Ricorda KISS : tienilo semplice, stupido , altrimenti potresti perderti la complessità.

    
risposta data 01.04.2013 - 06:04
fonte
1

Non sono d'accordo con un gruppo di persone più intelligenti di me e più esperte in sicurezza di me. Quindi probabilmente ho torto.

Improvvisare la propria funzione di hash è una buona idea - se lo fai bene. Segui questi 3 semplici passaggi.

  1. Identifica un punto debole o un difetto nelle funzioni di hash esistenti.
  2. Migliora la tua funzione di hash che non ha questo difetto.
  3. Verifica che la tua funzione di hash improvvisata abbia tutti i punti di forza delle funzioni di hash esistenti.

Dopo aver completato il passaggio 3, sarebbe un pazzo non utilizzare la tua funzione hash improvvisata.

    
risposta data 02.04.2013 - 02:05
fonte
0

Quando si concatenano diverse funzioni hash perché si è preoccupati di quello è vitale applicare il sale indietro prima di applicare l'algoritmo di hashing successivo, quindi eventuali punti deboli di collisione in uno degli algoritmi non comprometteranno la seconda funzione di hash che si è utilizzando:

scrypt (bcrypt (password + sale) + sale)

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

Quindi quanto segue avrebbe più senso, e combinerebbe la forza di Argon 2i, ma ricadrà su bcrypt se un futuro aggressore mostrasse come risolvere banalmente l'argon 2i, che attualmente è considerato difficile:

bcrypt (Argon2i (password + sale) + sale)

Ma è meno probabile che tu commetta un errore se lo fai semplicemente:

scrypt (password + sale) O bcrypt (password + sale)

Ricorda che la maggior parte delle violazioni sono dovute a errori umani nel codice, molto meglio tenerlo semplice e rivedere il codice con analisi dinamiche e statiche e revisori di codici umani, per assicurarsi che nessun attacco di SQL injection passi (Ricorda sempre parametrizza le tue query sul database!)

    
risposta data 13.12.2016 - 15:55
fonte
-2

Ho imparato molto con questo thread, quindi ho pensato in un modo per capire facilmente il risultato possibile quando combinato metodi diversi (ovviamente, non si adatta a tutti i casi):

[W = debole, S = strong]:

Formula        | Example
---------------|-----------------
W(W) = W       | md5(sha1(pwd))
W(S) = W       | md5(bcrypt(pwd))
S(W) = W       | bcrypt(md5(pwd)) 
S(S) = S-      | bcrypt(scrypt(pwd)) //their interaction is unknown
W+S = W        | md5(pwd)+brcypt(pwd) 
S+S = W        | bcrypt(pwd)+scrypt(pwd)
W'+S" = S      | md5(pwd1)+brcypt(pwd2) //pwd1 != pwd2
S'+S" = 2S     | bcrypt(pwd1)+scrypt(pwd2) //pwd1 != pwd2
SUBSTR(W) = W  | substr(md5(pwd),n); //Shorter the string, the weaker
SUBSTR(S) = S- | substr(bcrypt(pwd),n); //Shorter the string, the weaker
S+x = S+       | bcrypt(pwd)+x   //x=Variable str. unrelated to pwd
S(S+x) = S-    | bcrypt(scrypt(pwd)+x) //x=Variable str. unrelated to pwd
ROT13(S) = W   | ROT13(bcrypt(pwd))  //If Rot13 has a vulnerability: fail

Ciò che voglio ottenere qui è mostrare in modo semplice che tali combinazioni non aggiungano ulteriore sicurezza nella maggior parte dei casi (quindi la complessità aggiunta non è degna).

    
risposta data 13.04.2016 - 04:06
fonte

Leggi altre domande sui tag