MySQL ENCRYPT sale non è casuale?

3

So che al giorno d'oggi ci sono pochi motivi per usare ancora ENCRYPT() , ciò che con bcrypt è quasi onnipresente e MySQL fornisce hash migliori come SHA1 .

Ma mentre si dilettava con ENCRYPT() su MySQL 5.6.12 (OpenSuSE 13.1, x64), ho notato un'anomalia con il suo output che inizialmente attribuivo al pool di entropia esaurito (potrebbe potrebbe sono stati), e quindi probabilmente al sale trapelato tra le connessioni.

Dopo la verifica, non è stato il caso.

Piuttosto, il sale è derivato dal timestamp UNIX . Quindi due chiamate a ENCRYPT() nello stesso secondo produrranno sali identici e il sale si ripeterà esattamente ogni ora, 12 minuti, 16 secondi.

while(true); do \
    echo "SELECT NOW(),ENCRYPT('test');" \
    | mysql test | grep -v ENCRYPT; \
done | uniq -c

 54 2014-05-14 00:13:16     w5kVzeZkJCcqM
148 2014-05-14 00:13:17     x5/h3KfsBkEYk
150 2014-05-14 00:13:18     y5thvRDxwJgM6
145 2014-05-14 00:13:19     z5RL9IZ0..XH6
141 2014-05-14 00:13:20     .6asQOJRqB1i2
130 2014-05-14 00:13:21     /6J1nHNWbi6Ac
124 2014-05-14 00:13:22     06NT9j.EegRJs

Naturalmente posso fornire il mio sale e ottenerlo da una fonte casuale - TO_BASE64(CONCAT(CHAR(RAND()*256),CHAR(RAND()*256))) o qualcosa, che dovrebbe avere lo stesso intervallo di caratteri del sale originale nei suoi primi due byte, quindi ENCRYPT() sferzare un sale delle sue stesse esigenze è solo un ultimo sforzo.

E non ci si aspetta che il sale sia segreto , quindi essere conosciuto in anticipo non è forse un problema troppo grande.

Anche così, usare time() per la salatura non mi sembra una buona opzione ( questa risposta conferma anche questo).

Mi manca qualcosa di ovvio? Forse questo comportamento è configurabile in qualche modo (a parte la ricompilazione)? È una caratteristica nota (non sono stato in grado di trovare alcun riferimento su Google o MySQL KB)?

    
posta LSerni 14.05.2014 - 00:16
fonte

2 risposte

1

La funzione MySQL ENCRYPT () è molto mal chiamata, perché non è affatto encryption ; è un wrapper attorno alla funzione crypt() fornita dal sistema operativo, che è ugualmente mal chiamata. Questa è una funzione di hashing della password che sembra essere derivata dal codice a blocchi DES , ma "derivato" è la parola importante. L'algoritmo è stato modificato e, in particolare, la password fornita viene utilizzata come chiave di crittografia, non come dati da crittografare. In particolare, la trasformazione non è reversibile: non c'è decrypt() che rivelerebbe la password.

Accade così che la tradizionale funzione crypt() basata su DES, su cui ENCRYPT() si basa, includa un sale di 12 bit. Il punto di un sale è ripetersi tanto raramente quanto è fattibile. Tuttavia, con un sale a 12 bit, ci sono solo 4096 valori di sale possibili, quindi il riutilizzo di sale non può essere davvero evitato. D'altra parte, i sali non devono essere segreti o casuali o imprevedibili. Una strategia di allocazione basata sul tempo è buona come qualsiasi altra; la selezione casuale del sale non andrebbe molto meglio a tale riguardo. Quelle due chiamate nello stesso secondo indicano che lo stesso sale è un po 'trascurato; potrebbe ritorcersi contro se si deve usare ENCRYPT() in modo batch, per hash un sacco di password in breve tempo. A parte questo, non c'è niente di sbagliato nell'usare l'ora corrente come salt.

La vera debolezza, qui, sta usando la funzione crypt() basata su DES. Ha alcune gravi carenze:

  • Le password limitate a 8 caratteri ASCII (i caratteri successivi e il bit più alto di ogni byte vengono ignorati).
  • Spazio di sale molto piccolo che porta a un inevitabile riutilizzo del sale non trascurabile.
  • Via troppo veloce per il comfort: il numero interno di iterazioni è fisso e basso, e la funzione può essere velocizzata molto nel contesto di un attacco parallelo usando le tecniche di applicazione dei bit.

Se hai preso la decisione sfortunata di usare quella funzione, allora un sale basato sul tempo non aumenterà sensibilmente la tua sofferenza autoinflitta.

    
risposta data 10.06.2014 - 21:50
fonte
1

Guardando il sorgente mysql ( item_strfunc.cc ), sembra che tu sia corretto:

String *Item_func_encrypt::val_str(String *str) { ... #ifdef HAVE_CRYPT char salt[3],*salt_ptr; if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) return make_empty_result(); if (arg_count == 1) { // generate random salt time_t timestamp=current_thd->query_start(); salt[0] = bin_to_ascii( (ulong) timestamp & 0x3f); salt[1] = bin_to_ascii(( (ulong) timestamp >> 5) & 0x3f); salt[2] = 0; salt_ptr=salt; }

Il sale casuale viene generato direttamente dall'ora di avvio della query:

  • timestamp=current_thd->query_start()
risposta data 10.06.2014 - 21:17
fonte

Leggi altre domande sui tag