MySQL ENCRYPT ¿la sal no es aleatoria?

3

Sé que hay pocas razones para seguir usando ENCRYPT() hoy en día, ya que bcrypt es casi omnipresente y MySQL proporciona mejores hashes como SHA1 .

Pero mientras incursionaba con ENCRYPT() en MySQL 5.6.12 (OpenSuSE 13.1, x64), noté una anomalía en su salida que al principio atribuí a que se agotaba el grupo de entropía ( podría han sido), y luego posiblemente a la sal que se filtró entre las conexiones.

Tras la verificación, resultó no ser el caso.

Más bien, la sal se deriva de la marca de tiempo de UNIX . Por lo tanto, dos llamadas a ENCRYPT() en el mismo segundo producirán sales idénticas, y la sal se repite exactamente cada hora, 12 minutos, 16 segundos.

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

Por supuesto que puedo suministrar mi propia sal y obtenerla de una fuente aleatoria - TO_BASE64(CONCAT(CHAR(RAND()*256),CHAR(RAND()*256))) o algo así, que debería obtener el mismo rango de caracteres que la sal original en sus primeros dos bytes, por lo que ENCRYPT() 's batir una sal de sus propias necesidades sólo será un esfuerzo de última hora.

Y no se espera que la sal sea secreta , por lo que ser conocido de antemano quizás no sea un problema demasiado grande.

Aun así, usar time() para la salazón no me parece una muy buena opción ( esta respuesta también confirma esto).

¿Me estoy perdiendo algo obvio? ¿Es tal vez este comportamiento configurable de alguna manera (aparte de la recompilación)? ¿Es una característica conocida (no pude encontrar ninguna referencia en Google o MySQL KB)?

    
pregunta LSerni 14.05.2014 - 00:16
fuente

2 respuestas

1

La función MySQL ENCRYPT () tiene un nombre muy pobre, porque no es encriptación en absoluto; es una envoltura alrededor de la función crypt() provista por el sistema operativo, que tiene el mismo nombre. Esta es una función de hash de contraseña que se deriva del cifrado de bloque DES , pero "derivado" es la palabra importante. El algoritmo se ha modificado y, en particular, la contraseña proporcionada se utiliza como clave de cifrado, no como datos que deben cifrarse. En particular, la transformación no es reversible: no hay decrypt() que revelaría la contraseña.

Sucede que la función tradicional crypt() basada en DES, sobre la que se basa ENCRYPT() , incluye una sal de 12 bits. El punto de una sal es repetirse tan raramente como sea posible. Sin embargo, con una sal de 12 bits, solo hay 4096 valores de sal posibles, por lo que la reutilización de la sal no se puede evitar. Por otro lado, las sales no necesitan ser secretas, aleatorias o impredecibles. Una estrategia de asignación basada en el tiempo es tan buena como cualquier otra; La selección aleatoria de sal no sería mucho mejor en ese sentido. Que dos llamadas dentro del mismo segundo impliquen que la misma sal es algo descuidada; puede ser contraproducente si tiene que usar ENCRYPT() de forma discontinua, para hash muchas contraseñas en poco tiempo. Aparte de eso, no hay nada realmente malo en usar la hora actual como sal.

La verdadera debilidad, en este caso, es usar la función crypt() basada en DES. Tiene algunas deficiencias serias:

  • Las contraseñas limitadas a 8 caracteres ASCII (los caracteres posteriores y el bit alto de cada byte, se ignoran).
  • Espacio de sal muy pequeño que conduce a una inevitable reutilización de sal no despreciable.
  • Demasiado rápido para la comodidad: el número interno de iteración es fijo y bajo, y la función se puede acelerar mucho en el contexto de un ataque paralelo mediante el uso de técnicas de corte de bits.

Si has tomado la desafortunada decisión de usar esa función, entonces una sal basada en el tiempo no aumentará notablemente tu miseria autoinfligida.

    
respondido por el Thomas Pornin 10.06.2014 - 21:50
fuente
1

Mirando la fuente mysql ( item_strfunc.cc ), parece que estás en lo correcto:

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; }

La sal aleatoria se genera directamente desde la hora de inicio de la consulta:

  • timestamp=current_thd->query_start()
respondido por el Ari Trachtenberg 10.06.2014 - 21:17
fuente

Lea otras preguntas en las etiquetas