284 votos

¿Es "doble hashing" una menos segura que sólo una vez lo hash de contraseña?

Es un hash de la contraseña dos veces antes de de almacenamiento más o menos seguro que sólo hash de una vez?

Lo que yo estoy hablando es de hacer esto:

$hashed_password = hash(hash($plaintext_password));

en lugar de esto:

$hashed_password = hash($plaintext_password);

Si es menos seguro, puede proporcionar una buena explicación (o un enlace a uno)?

También, hace la función hash usada hacer una diferencia? No hace ninguna diferencia si se mezcla el md5 y sha1 (por ejemplo) en lugar de repetir la misma función hash?

Nota 1: Cuando digo "doble hashing" estoy hablando de hash de una contraseña dos veces en un intento de hacer más oscurecida. No estoy hablando acerca de la técnica para la resolución de colisiones.

Nota 2: yo sé que tengo que agregar al azar de sal para que sea realmente seguro. La pregunta es si el hash dos veces con el mismo algoritmo que ayuda o perjudica el hash.

255voto

erickson Puntos 127945

Hash de una contraseña una vez que es inseguro

No, de múltiples hash no son menos seguras; son una parte esencial de la seguridad de la utilización de la contraseña.

Recorrer el hash aumenta el tiempo que se toma para un atacante intentar cada contraseña en su lista de candidatos. Fácilmente se puede aumentar el tiempo que se tarda para atacar una contraseña de horas a años.

Iteración Simple no es suficiente

Simplemente encadenamiento de hash de salida a entrada no es suficiente para la seguridad. La iteración debe tener lugar en el contexto de un algoritmo que conserva la entropía de la contraseña. Por suerte, hay varios algoritmos publicados que han tenido el suficiente control para dar confianza en su diseño.

Una buena clave derivación del algoritmo como PBKDF2 inyecta la contraseña en cada ronda de la mezcla, la mitigación de las preocupaciones acerca de las colisiones de hash de salida. PBKDF2 puede ser utilizado para la autenticación de contraseña tal como es. Bcrypt sigue la clave de derivación con un cifrado de paso, de esa manera, si es una manera rápida para revertir la clave de la derivación es descubierto, un atacante todavía tiene que completar un ataque de texto claro.

Cómo romper una contraseña

Las contraseñas almacenadas se necesitan protección frente a un ataque sin conexión. Si las contraseñas no salado, que se puede romper con un pre-calculadas ataque de diccionario (por ejemplo, utilizando una Tabla de cadenas arco iris). De lo contrario, el atacante debe pasar el tiempo para calcular un hash para cada uno de contraseña y ver si coincide con el hash almacenado.

Todas las contraseñas que no son igualmente probables. Los atacantes podrían buscamos todas las contraseñas cortas, pero ellos saben que sus posibilidades de fuerza bruta éxito caer bruscamente con cada personaje. En lugar de eso, se utiliza un listado ordenado de las más probabilidades de contraseñas. Comienzan con "password123" y el progreso de los usa con menos frecuencia las contraseñas.

Digamos que una atacantes lista es larga, con 10 mil millones de candidatos; supongamos también que un sistema de escritorio puede calcular 1 millones de hashes por segundo. El atacante puede poner a prueba toda su lista es de menos de tres horas si sólo una iteración se utiliza. Pero si tan sólo 2000 iteraciones se utilizan, que el tiempo se extiende a casi 8 meses. Para derrotar a un más sofisticado atacante capaz de descargar un programa que puede aprovechar el poder de sus GPU, por ejemplo-es necesario realizar más iteraciones.

¿Cuánto es suficiente?

El número de iteraciones para el uso de un trade-off entre la seguridad y la experiencia del usuario. Hardware especializado que puede ser utilizado por los atacantes es barato, pero todavía se pueden realizar cientos de millones de iteraciones por segundo. El rendimiento del atacante del sistema determina el tiempo que se necesita para romper una contraseña, dado un número de iteraciones. Pero su aplicación no es probable que el uso de este hardware especializado. Cuántas iteraciones se puede realizar sin agravantes de los usuarios depende de su sistema.

Usted probablemente puede permitir a los usuarios esperar un extra de ¾ de segundo durante la autenticación. Perfil de la plataforma destino, y usar tantas iteraciones como usted puede permitirse. Plataformas que he probado (un usuario en un dispositivo móvil, o número de usuarios en una plataforma de servidor) puede soportar cómodamente PBKDF2 con entre 60.000 y 120.000 iteraciones, o bcrypt con factor de costo de 12 o 13 años.

Más antecedentes

Leer PKCS #5 para información fidedigna sobre el papel de la sal y iteraciones en la mezcla. Aunque PBKDF2 estaba destinado para la generación de claves de cifrado de contraseñas, funciona bien como un one-way hash de la contraseña de autenticación. En cada iteración de bcrypt es más caro que un SHA-2 hash, así que usted puede utilizar menor número de iteraciones, pero la idea es la misma. Bcrypt también va un paso más allá de la mayoría de los PBKDF2 basado en soluciones mediante el uso de la derivada clave para cifrar un conocido texto sin formato. El texto cifrado resultante se almacena como el "hash", junto con algunos de los meta-datos. Sin embargo, nada te detiene de hacer lo mismo con PBKDF2.

Aquí son otras de las respuestas que he escrito en este tema:

220voto

ircmaxell Puntos 74865

Yo casi no quieres contestar, porque todo el mundo con una respuesta aquí es correcto hasta cierto punto. Pero también me parece que todo el mundo también es incorrecto en algún grado también.

Para aquellos que dicen que es seguro, están en lo correcto en general. El "doble" de hash (o la lógica de expansión de eso, la iteración de una función hash) es absolutamente segura si se hace bien, por una preocupación específica.

Para aquellos que dicen que es inseguro, están en lo correcto en este caso. El código que se registra en la pregunta es inseguro. Vamos a hablar de por qué:

$hashed_password1 = md5( md5( plaintext_password ) );
$hashed_password2 = md5( plaintext_password );

Hay dos propiedades fundamentales de una función hash que nos preocupa:

  1. Pre-Imagen de la Resistencia - Dado un hash $h, debe ser difícil encontrar un mensaje $m tal que $h === hash($m)

  2. Segunda Pre-Imagen de la Resistencia - Dado un mensaje $m1, debe ser difícil encontrar un mensaje diferente $m2 tal que hash($m1) === hash($m2)

  3. Resistencia al choque - debe ser difícil encontrar un par de mensajes ($m1, $m2) tal que hash($m1) === hash($m2) (tenga en cuenta que esto es similar a la Segunda Pre-Imagen de la resistencia, pero diferente, ya que aquí el atacante tiene el control sobre ambos mensajes)...

Para el almacenamiento de las contraseñas, todo lo que realmente importa es la Pre-Imagen de la Resistencia. Los otros dos sería discutible, porque $m1 es la contraseña del usuario que estamos tratando de mantenerse a salvo. Así que si el atacante ya tiene, el hash no tiene nada que proteger...

DESCARGO de responsabilidad

Todo lo que sigue está basado en la premisa de que todos los que nos preocupamos es la Pre-Imagen de la Resistencia. Las otras dos propiedades fundamentales de las funciones de hash no puede (y no) en el mismo camino. Así que las conclusiones en este post son sólo aplicables cuando use las funciones de hash para el almacenamiento de contraseñas. No son aplicables en general...

Vamos a empezar

Por el bien de esta discusión, vamos a inventar nuestra propia función hash:

function ourHash($input) {
    $result = 0;
    for ($i = 0; $i < strlen($input); $i++) {
        $result += ord($input[$i]);
    }
    return (string) ($result % 256);
}

Ahora debería ser bastante obvio lo que esta función hash. Suma juntos los valores ASCII de cada carácter de la entrada y, a continuación, toma el modulo de ese resultado con 256.

Así que vamos a probarlo:

var_dump(
    ourHash('abc'), // string(2) "38"
    ourHash('def'), // string(2) "47"
    ourHash('hij'), // string(2) "59"
    ourHash('klm')  // string(2) "68"
);

Ahora, vamos a ver qué pasa si lo hacemos correr un par de veces alrededor de una función:

$tests = array(
    "abc",
    "def",
    "hij",
    "klm",
);

foreach ($tests as $test) {
    $hash = $test;
    for ($i = 0; $i < 100; $i++) {
        $hash = ourHash($hash);
    }
    echo "Hashing $test => $hash\n";
}

Que salidas:

Hashing abc => 152
Hashing def => 152
Hashing hij => 155
Hashing klm => 155

Gestión de recursos humanos, wow. Hemos generado colisiones!!! Vamos a tratar de buscar en la razón:

Aquí está la salida de hash de una cadena de cada posible hash de salida:

Hashing 0 => 48
Hashing 1 => 49
Hashing 2 => 50
Hashing 3 => 51
Hashing 4 => 52
Hashing 5 => 53
Hashing 6 => 54
Hashing 7 => 55
Hashing 8 => 56
Hashing 9 => 57
Hashing 10 => 97
Hashing 11 => 98
Hashing 12 => 99
Hashing 13 => 100
Hashing 14 => 101
Hashing 15 => 102
Hashing 16 => 103
Hashing 17 => 104
Hashing 18 => 105
Hashing 19 => 106
Hashing 20 => 98
Hashing 21 => 99
Hashing 22 => 100
Hashing 23 => 101
Hashing 24 => 102
Hashing 25 => 103
Hashing 26 => 104
Hashing 27 => 105
Hashing 28 => 106
Hashing 29 => 107
Hashing 30 => 99
Hashing 31 => 100
Hashing 32 => 101
Hashing 33 => 102
Hashing 34 => 103
Hashing 35 => 104
Hashing 36 => 105
Hashing 37 => 106
Hashing 38 => 107
Hashing 39 => 108
Hashing 40 => 100
Hashing 41 => 101
Hashing 42 => 102
Hashing 43 => 103
Hashing 44 => 104
Hashing 45 => 105
Hashing 46 => 106
Hashing 47 => 107
Hashing 48 => 108
Hashing 49 => 109
Hashing 50 => 101
Hashing 51 => 102
Hashing 52 => 103
Hashing 53 => 104
Hashing 54 => 105
Hashing 55 => 106
Hashing 56 => 107
Hashing 57 => 108
Hashing 58 => 109
Hashing 59 => 110
Hashing 60 => 102
Hashing 61 => 103
Hashing 62 => 104
Hashing 63 => 105
Hashing 64 => 106
Hashing 65 => 107
Hashing 66 => 108
Hashing 67 => 109
Hashing 68 => 110
Hashing 69 => 111
Hashing 70 => 103
Hashing 71 => 104
Hashing 72 => 105
Hashing 73 => 106
Hashing 74 => 107
Hashing 75 => 108
Hashing 76 => 109
Hashing 77 => 110
Hashing 78 => 111
Hashing 79 => 112
Hashing 80 => 104
Hashing 81 => 105
Hashing 82 => 106
Hashing 83 => 107
Hashing 84 => 108
Hashing 85 => 109
Hashing 86 => 110
Hashing 87 => 111
Hashing 88 => 112
Hashing 89 => 113
Hashing 90 => 105
Hashing 91 => 106
Hashing 92 => 107
Hashing 93 => 108
Hashing 94 => 109
Hashing 95 => 110
Hashing 96 => 111
Hashing 97 => 112
Hashing 98 => 113
Hashing 99 => 114
Hashing 100 => 145
Hashing 101 => 146
Hashing 102 => 147
Hashing 103 => 148
Hashing 104 => 149
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 146
Hashing 111 => 147
Hashing 112 => 148
Hashing 113 => 149
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 147
Hashing 121 => 148
Hashing 122 => 149
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 148
Hashing 131 => 149
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 149
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 160
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 160
Hashing 179 => 161
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 160
Hashing 188 => 161
Hashing 189 => 162
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 160
Hashing 197 => 161
Hashing 198 => 162
Hashing 199 => 163
Hashing 200 => 146
Hashing 201 => 147
Hashing 202 => 148
Hashing 203 => 149
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 147
Hashing 211 => 148
Hashing 212 => 149
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 148
Hashing 221 => 149
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 149
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

Aviso de la tendencia hacia los números más altos. Que resulta ser nuestro deadfall. Ejecuta el hash 4 veces ($hash = ourHash($hash)`, para cada elemento) vientos de hasta nos da:

Hashing 0 => 153
Hashing 1 => 154
Hashing 2 => 155
Hashing 3 => 156
Hashing 4 => 157
Hashing 5 => 158
Hashing 6 => 150
Hashing 7 => 151
Hashing 8 => 152
Hashing 9 => 153
Hashing 10 => 157
Hashing 11 => 158
Hashing 12 => 150
Hashing 13 => 154
Hashing 14 => 155
Hashing 15 => 156
Hashing 16 => 157
Hashing 17 => 158
Hashing 18 => 150
Hashing 19 => 151
Hashing 20 => 158
Hashing 21 => 150
Hashing 22 => 154
Hashing 23 => 155
Hashing 24 => 156
Hashing 25 => 157
Hashing 26 => 158
Hashing 27 => 150
Hashing 28 => 151
Hashing 29 => 152
Hashing 30 => 150
Hashing 31 => 154
Hashing 32 => 155
Hashing 33 => 156
Hashing 34 => 157
Hashing 35 => 158
Hashing 36 => 150
Hashing 37 => 151
Hashing 38 => 152
Hashing 39 => 153
Hashing 40 => 154
Hashing 41 => 155
Hashing 42 => 156
Hashing 43 => 157
Hashing 44 => 158
Hashing 45 => 150
Hashing 46 => 151
Hashing 47 => 152
Hashing 48 => 153
Hashing 49 => 154
Hashing 50 => 155
Hashing 51 => 156
Hashing 52 => 157
Hashing 53 => 158
Hashing 54 => 150
Hashing 55 => 151
Hashing 56 => 152
Hashing 57 => 153
Hashing 58 => 154
Hashing 59 => 155
Hashing 60 => 156
Hashing 61 => 157
Hashing 62 => 158
Hashing 63 => 150
Hashing 64 => 151
Hashing 65 => 152
Hashing 66 => 153
Hashing 67 => 154
Hashing 68 => 155
Hashing 69 => 156
Hashing 70 => 157
Hashing 71 => 158
Hashing 72 => 150
Hashing 73 => 151
Hashing 74 => 152
Hashing 75 => 153
Hashing 76 => 154
Hashing 77 => 155
Hashing 78 => 156
Hashing 79 => 157
Hashing 80 => 158
Hashing 81 => 150
Hashing 82 => 151
Hashing 83 => 152
Hashing 84 => 153
Hashing 85 => 154
Hashing 86 => 155
Hashing 87 => 156
Hashing 88 => 157
Hashing 89 => 158
Hashing 90 => 150
Hashing 91 => 151
Hashing 92 => 152
Hashing 93 => 153
Hashing 94 => 154
Hashing 95 => 155
Hashing 96 => 156
Hashing 97 => 157
Hashing 98 => 158
Hashing 99 => 150
Hashing 100 => 154
Hashing 101 => 155
Hashing 102 => 156
Hashing 103 => 157
Hashing 104 => 158
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 155
Hashing 111 => 156
Hashing 112 => 157
Hashing 113 => 158
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 156
Hashing 121 => 157
Hashing 122 => 158
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 157
Hashing 131 => 158
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 158
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 151
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 151
Hashing 179 => 152
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 151
Hashing 188 => 152
Hashing 189 => 153
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 151
Hashing 197 => 152
Hashing 198 => 153
Hashing 199 => 154
Hashing 200 => 155
Hashing 201 => 156
Hashing 202 => 157
Hashing 203 => 158
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 156
Hashing 211 => 157
Hashing 212 => 158
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 157
Hashing 221 => 158
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 158
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

Hemos reducido nosotros mismos a 8 valores... Eso es malo... Nuestra función original asignado S(∞) a S(256). Que es que hemos creado un Surjective Función de mapeo $input a $output.

Ya tenemos un Surjective función, no tenemos garantías de la asignación de cualquier subconjunto de la entrada no tiene choques (de hecho, en la práctica).

Eso es lo que pasó aquí! Nuestra función era malo, pero no es por eso que esta trabajado (por eso funcionó tan rápidamente y tan completamente).

Lo mismo sucede con MD5. Asigna S(∞) a S(2^128). Ya que no hay ninguna garantía de que la ejecución MD5(S(output)) será Inyectiva, lo que significa que no tengan colisiones.

TL/DR Sección

Por lo tanto, desde la alimentación de la salida de regreso a md5 directamente puede generar colisiones, cada iteración va a aumentar la probabilidad de colisiones. Este es un aumento lineal sin embargo, lo que significa que mientras que el conjunto de resultados de 2^128 se reduce, no se redujo significativamente con la rapidez suficiente para ser un defecto crítico.

Así que,

$output = md5($input); // 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities

Las más veces que recorrer, más que la reducción de la va.

La Solución

Afortunadamente para nosotros, hay un trivial forma de solucionar este problema: Feed back algo en el más iteraciones:

$output = md5($input); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities    

Tenga en cuenta que las nuevas versiones no son 2^128 para cada valor de $input. Lo que significa que podemos ser capaces de generar $input valores que todavía chocan abajo de la línea (y, por tanto, se asentará o resonar en mucho menos de 2^128 posibles salidas). Pero el caso general, para $input sigue siendo tan fuerte como lo era para una sola ronda.

Espera, ¿a que no? Vamos a probar esto con nuestro ourHash() función. El cambio a $hash = ourHash($input . $hash);, para 100 iteraciones:

Hashing 0 => 201
Hashing 1 => 212
Hashing 2 => 199
Hashing 3 => 201
Hashing 4 => 203
Hashing 5 => 205
Hashing 6 => 207
Hashing 7 => 209
Hashing 8 => 211
Hashing 9 => 204
Hashing 10 => 251
Hashing 11 => 147
Hashing 12 => 251
Hashing 13 => 148
Hashing 14 => 253
Hashing 15 => 0
Hashing 16 => 1
Hashing 17 => 2
Hashing 18 => 161
Hashing 19 => 163
Hashing 20 => 147
Hashing 21 => 251
Hashing 22 => 148
Hashing 23 => 253
Hashing 24 => 0
Hashing 25 => 1
Hashing 26 => 2
Hashing 27 => 161
Hashing 28 => 163
Hashing 29 => 8
Hashing 30 => 251
Hashing 31 => 148
Hashing 32 => 253
Hashing 33 => 0
Hashing 34 => 1
Hashing 35 => 2
Hashing 36 => 161
Hashing 37 => 163
Hashing 38 => 8
Hashing 39 => 4
Hashing 40 => 148
Hashing 41 => 253
Hashing 42 => 0
Hashing 43 => 1
Hashing 44 => 2
Hashing 45 => 161
Hashing 46 => 163
Hashing 47 => 8
Hashing 48 => 4
Hashing 49 => 9
Hashing 50 => 253
Hashing 51 => 0
Hashing 52 => 1
Hashing 53 => 2
Hashing 54 => 161
Hashing 55 => 163
Hashing 56 => 8
Hashing 57 => 4
Hashing 58 => 9
Hashing 59 => 11
Hashing 60 => 0
Hashing 61 => 1
Hashing 62 => 2
Hashing 63 => 161
Hashing 64 => 163
Hashing 65 => 8
Hashing 66 => 4
Hashing 67 => 9
Hashing 68 => 11
Hashing 69 => 4
Hashing 70 => 1
Hashing 71 => 2
Hashing 72 => 161
Hashing 73 => 163
Hashing 74 => 8
Hashing 75 => 4
Hashing 76 => 9
Hashing 77 => 11
Hashing 78 => 4
Hashing 79 => 3
Hashing 80 => 2
Hashing 81 => 161
Hashing 82 => 163
Hashing 83 => 8
Hashing 84 => 4
Hashing 85 => 9
Hashing 86 => 11
Hashing 87 => 4
Hashing 88 => 3
Hashing 89 => 17
Hashing 90 => 161
Hashing 91 => 163
Hashing 92 => 8
Hashing 93 => 4
Hashing 94 => 9
Hashing 95 => 11
Hashing 96 => 4
Hashing 97 => 3
Hashing 98 => 17
Hashing 99 => 13
Hashing 100 => 246
Hashing 101 => 248
Hashing 102 => 49
Hashing 103 => 44
Hashing 104 => 255
Hashing 105 => 198
Hashing 106 => 43
Hashing 107 => 51
Hashing 108 => 202
Hashing 109 => 2
Hashing 110 => 248
Hashing 111 => 49
Hashing 112 => 44
Hashing 113 => 255
Hashing 114 => 198
Hashing 115 => 43
Hashing 116 => 51
Hashing 117 => 202
Hashing 118 => 2
Hashing 119 => 51
Hashing 120 => 49
Hashing 121 => 44
Hashing 122 => 255
Hashing 123 => 198
Hashing 124 => 43
Hashing 125 => 51
Hashing 126 => 202
Hashing 127 => 2
Hashing 128 => 51
Hashing 129 => 53
Hashing 130 => 44
Hashing 131 => 255
Hashing 132 => 198
Hashing 133 => 43
Hashing 134 => 51
Hashing 135 => 202
Hashing 136 => 2
Hashing 137 => 51
Hashing 138 => 53
Hashing 139 => 55
Hashing 140 => 255
Hashing 141 => 198
Hashing 142 => 43
Hashing 143 => 51
Hashing 144 => 202
Hashing 145 => 2
Hashing 146 => 51
Hashing 147 => 53
Hashing 148 => 55
Hashing 149 => 58
Hashing 150 => 198
Hashing 151 => 43
Hashing 152 => 51
Hashing 153 => 202
Hashing 154 => 2
Hashing 155 => 51
Hashing 156 => 53
Hashing 157 => 55
Hashing 158 => 58
Hashing 159 => 0
Hashing 160 => 43
Hashing 161 => 51
Hashing 162 => 202
Hashing 163 => 2
Hashing 164 => 51
Hashing 165 => 53
Hashing 166 => 55
Hashing 167 => 58
Hashing 168 => 0
Hashing 169 => 209
Hashing 170 => 51
Hashing 171 => 202
Hashing 172 => 2
Hashing 173 => 51
Hashing 174 => 53
Hashing 175 => 55
Hashing 176 => 58
Hashing 177 => 0
Hashing 178 => 209
Hashing 179 => 216
Hashing 180 => 202
Hashing 181 => 2
Hashing 182 => 51
Hashing 183 => 53
Hashing 184 => 55
Hashing 185 => 58
Hashing 186 => 0
Hashing 187 => 209
Hashing 188 => 216
Hashing 189 => 219
Hashing 190 => 2
Hashing 191 => 51
Hashing 192 => 53
Hashing 193 => 55
Hashing 194 => 58
Hashing 195 => 0
Hashing 196 => 209
Hashing 197 => 216
Hashing 198 => 219
Hashing 199 => 220
Hashing 200 => 248
Hashing 201 => 49
Hashing 202 => 44
Hashing 203 => 255
Hashing 204 => 198
Hashing 205 => 43
Hashing 206 => 51
Hashing 207 => 202
Hashing 208 => 2
Hashing 209 => 51
Hashing 210 => 49
Hashing 211 => 44
Hashing 212 => 255
Hashing 213 => 198
Hashing 214 => 43
Hashing 215 => 51
Hashing 216 => 202
Hashing 217 => 2
Hashing 218 => 51
Hashing 219 => 53
Hashing 220 => 44
Hashing 221 => 255
Hashing 222 => 198
Hashing 223 => 43
Hashing 224 => 51
Hashing 225 => 202
Hashing 226 => 2
Hashing 227 => 51
Hashing 228 => 53
Hashing 229 => 55
Hashing 230 => 255
Hashing 231 => 198
Hashing 232 => 43
Hashing 233 => 51
Hashing 234 => 202
Hashing 235 => 2
Hashing 236 => 51
Hashing 237 => 53
Hashing 238 => 55
Hashing 239 => 58
Hashing 240 => 198
Hashing 241 => 43
Hashing 242 => 51
Hashing 243 => 202
Hashing 244 => 2
Hashing 245 => 51
Hashing 246 => 53
Hashing 247 => 55
Hashing 248 => 58
Hashing 249 => 0
Hashing 250 => 43
Hashing 251 => 51
Hashing 252 => 202
Hashing 253 => 2
Hashing 254 => 51
Hashing 255 => 53

Todavía hay un duro patrón de allí, pero tenga en cuenta que hay más de un patrón de nuestra función subyacente (que ya era bastante débil).

Observe, sin embargo, que 0 y 3 se convirtió en colisiones, aunque no estuvieran en la misma tirada. Eso es una aplicación de lo que he dicho antes (que la resistencia al choque se mantiene la misma para el conjunto de todas las entradas, pero específico de la colisión de las rutas se pueden abrir debido a fallas en el algoritmo subyacente).

TL/DR Sección

De alimentación por la entrada en cada iteración, que efectivamente romper cualquier colisiones que podrían haber ocurrido en la anterior iteración.

Por lo tanto, md5($input . md5($input)); debe ser (en teoría al menos) tan fuerte como md5($input).

Esto Es Importante?

Sí. Esta es una de las razones por las que PBKDF2 sustituido PBKDF1 en el RFC 2898. Considere el interior de los bucles de las dos::

PBKDF1:

T_1 = Hash (P || S) ,
T_2 = Hash (T_1) ,
...
T_c = Hash (T_{c-1}) 

Donde c es el número de iteraciones, P es la Contraseña y S es la sal

PBKDF2:

U_1 = PRF (P, S || INT (i)) ,
U_2 = PRF (P, U_1) ,
...
U_c = PRF (P, U_{c-1})

Donde PRF es realmente sólo una HMAC. Pero para nuestros propósitos aquí, vamos a decir que PRF(P, S) = Hash(P || S) (es decir, el PRF de 2 entradas es el mismo, hablando a grandes rasgos, como hash con los dos concatenados). Es mucho no, pero para nuestros propósitos es.

Así PBKDF2 mantiene la resistencia al choque de la subyacentes Hash de la función, donde PBKDF1 no.

Tie-ción de Todos Juntos:

Sabemos de seguro maneras de recorrer en un hash. De hecho:

$hash = $input;
$i = 10000;
do {
   $hash = hash($input . $hash);
} while ($i-- > 0);

Es normalmente segura.

Ahora, para ir a por qué queremos hash, vamos a analizar la entropía movimiento.

Un hash toma en el conjunto infinito: S(∞) y produce un menor, de un tamaño consistente set S(n). La siguiente iteración (suponiendo que la entrada se pasa en los mapas S(∞) a S(n) de nuevo:

S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)

Observe que el resultado final tiene exactamente la misma cantidad de entropía como el primero. La iteración se no se "hacen que sea más oscuro". La entropía es idéntico. No hay magia fuente de imprevisibilidad (es un Pseudo-Aleatorio de la Función, no una Función Aleatoria).

Sin embargo, hay una ganancia para la iteración. Esto hace que el hash proceso artificialmente más lento. Y es por eso que la iteración puede ser una buena idea. De hecho, es el principio básico de la mayoría de los modernos contraseña de algoritmos de hash (el hecho de que haciendo algo más-y-más lo hace más lento).

Lento es bueno, porque es la lucha contra la principal amenaza para la seguridad: la fuerza bruta. El más lento hacemos nuestro algoritmo de hash, más difícil que los atacantes tienen que trabajar para atacar a los hash de las contraseñas robadas de nosotros. Y eso es una buena cosa!!!

48voto

orip Puntos 28225

Sí, re-hash reduce el espacio de búsqueda, pero no, no importa - la reducción efectiva es insignificante.

Remake aumenta el tiempo que se toma a la fuerza bruta, pero hacerlo dos veces es también deficiente.

Lo que realmente desea es el hash de la contraseña con PBKDF2 - un método probado de usar un hash seguro con sal y iteraciones. Echa un vistazo a este MODO de respuesta.

EDIT: casi se me olvida - NO UTILIZAR MD5!!!! El uso de un moderno hash de cifrado, tales como el SHA-2 de la familia (SHA-256, SHA-384, y SHA-512).

10voto

Rich Bradshaw Puntos 33598

Sí - se reduce el número de posibles cadenas que coinciden con la cadena.

Como ya se ha mencionado, salted hash son mucho mejores.

Un artículo aquí: http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/, los intentos de una prueba de por qué es equivalente, pero no estoy seguro de que con la lógica. En parte se supone que no hay software disponible para analizar md5(md5(texto)), pero, obviamente, es bastante trivial para producir las tablas rainbow.

Todavía estoy pegando con mi respuesta de que hay menor número de md5(md5(texto)) tipo de hash de md5(texto) hashes, aumentando las probabilidades de colisión (aunque todavía a un improbable probabilidad) y reducir el espacio de búsqueda.

2voto

Ben Daniel Puntos 2570

Personalmente no me molestaría con múltiples hashses, pero me aseguraría a discutir también el nombre de usuario (o en otro campo de ID de usuario) así como la contraseña para que dos usuarios con la misma contraseña no terminan con el mismo hash. También probablemente lanzaría otra cadena algunas constante en la cadena de entrada también para la buena medida.

$hashed_password = md5( "xxx" + "|" + user_name + "|" + plaintext_password);

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X