Является ли «двойное хеширование» паролем менее безопасным, чем однократное хеширование?

293

Является ли хеширование пароля дважды перед хранением более или менее безопасным, чем простое хеширование?

То, о чем я говорю, делает это:

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

вместо этого:

$hashed_password = hash($plaintext_password);

Если это менее безопасно, можете ли вы дать хорошее объяснение (или ссылку на него)?

Кроме того, используемая хэш-функция имеет значение? Есть ли какая-то разница, если вы смешаете md5 и sha1 (например) вместо того, чтобы повторять одну и ту же хеш-функцию?

Примечание 1: Когда я говорю «двойное хеширование», я говорю о хешировании пароля дважды, чтобы сделать его более скрытым. Я не говорю о технике разрешения коллизий .

Примечание 2: я знаю, что мне нужно добавить случайную соль, чтобы действительно обеспечить ее безопасность. Вопрос в том, помогает ли хеширование дважды с помощью одного и того же алгоритма или вредит хешу.

Билл Ящерица
источник
2
Hash(password)и Hash(Hash(password))одинаково небезопасны. У обоих отсутствует понятие семантической безопасности . То есть, выходной сигнал является отличимым от случайного. Например, MD5("password")есть 5f4dcc3b5aa765d61d8327deb882cf99. Я знаю, что это хэш MD5 password, и его можно отличить от случайного. Вместо этого вы должны использовать HMAC. Это доказуемо безопасно и его PRF.
jww

Ответы:

267

Хеширование пароля один раз небезопасно

Нет, несколько хешей не менее безопасны; они являются неотъемлемой частью безопасного использования пароля.

Повторение хэша увеличивает время, которое требуется злоумышленнику для проверки каждого пароля в своем списке кандидатов. Вы можете легко увеличить время, необходимое для атаки на пароль, с часов до лет.

Простая итерация не достаточно

Простая цепочка вывода хеша для ввода недостаточна для безопасности. Итерация должна происходить в контексте алгоритма, который сохраняет энтропию пароля. К счастью, есть несколько опубликованных алгоритмов, которые были достаточно изучены, чтобы придать уверенность в их дизайне.

Хороший алгоритм получения ключей, такой как PBKDF2, вводит пароль в каждый раунд хэширования, уменьшая опасения по поводу коллизий при выводе хэша. PBKDF2 может использоваться для аутентификации по паролю как есть. Bcrypt следует за получением ключа с шагом шифрования; таким образом, если обнаружен быстрый способ обратить вспять процесс деривации ключа, злоумышленнику все равно придется выполнить атаку с использованием открытого текста.

Как взломать пароль

Сохраненные пароли нуждаются в защите от автономной атаки. Если пароли не засолены, их можно взломать с помощью заранее вычисленной атаки по словарю (например, с использованием таблицы Rainbow). В противном случае злоумышленник должен потратить время на вычисление хэша для каждого пароля и посмотреть, соответствует ли он сохраненному хешу.

Все пароли не одинаково вероятны. Злоумышленники могут провести тщательный поиск по всем коротким паролям, но они знают, что их шансы на успех грубой силой резко уменьшаются с каждым дополнительным персонажем. Вместо этого они используют упорядоченный список наиболее вероятных паролей. Они начинаются с «password123» и переходят к менее часто используемым паролям.

Допустим, список атакующих длинный, с 10 миллиардами кандидатов; Предположим также, что настольная система может вычислять 1 миллион хешей в секунду. Атакующий может проверить весь свой список менее чем за три часа, если используется только одна итерация. Но если используется всего 2000 итераций, это время увеличивается почти до 8 месяцев. Чтобы победить более искушенного злоумышленника - например, способного загрузить программу, которая может использовать мощь своего графического процессора, - вам нужно больше итераций.

Сколько достаточно?

Количество используемых итераций является компромиссом между безопасностью и опытом пользователя. Специализированное оборудование, которое может быть использовано злоумышленниками, дешево, но оно может выполнять сотни миллионов итераций в секунду. Производительность системы злоумышленника определяет, сколько времени потребуется, чтобы взломать пароль, учитывая количество итераций. Но ваше приложение вряд ли будет использовать это специализированное оборудование. Сколько итераций вы можете выполнить, не раздражая пользователей, зависит от вашей системы.

Вы, вероятно, можете позволить пользователям ждать больше ¾ секунды или около того во время аутентификации. Профилируйте свою целевую платформу и используйте столько итераций, сколько можете себе позволить. Платформы, которые я тестировал (один пользователь на мобильном устройстве или много пользователей на серверной платформе), могут с удобством поддерживать PBKDF2 с 60 000 и 120 000 итераций или bcrypt с коэффициентом стоимости 12 или 13.

Больше фона

Прочтите PKCS # 5 для получения достоверной информации о роли соли и итераций в хешировании. Несмотря на то, что PBKDF2 предназначался для генерации ключей шифрования из паролей, он хорошо работает как односторонний хэш для аутентификации по паролю. Каждая итерация bcrypt дороже, чем хеш SHA-2, поэтому вы можете использовать меньше итераций, но идея та же. Bcrypt также выходит за рамки большинства решений на основе PBKDF2, используя производный ключ для шифрования широко известного простого текста. Полученный зашифрованный текст сохраняется как «хэш» вместе с некоторыми метаданными. Однако ничто не мешает вам делать то же самое с PBKDF2.

Вот другие ответы, которые я написал на эту тему:

Эриксон
источник
68
Намеренное создание медленного алгоритма является общепринятой практикой, когда вы пытаетесь предотвратить словарные атаки на скомпрометированные хранилища аутентификации. Техника называется «усиление ключа» или «растяжение ключа». См en.wikipedia.org/wiki/Key_stretching
17
@RoBorg: неважно, насколько медленной будет ваша реализация, но насколько медленной будет реализация злоумышленника: если сам хеш будет в тысячи раз медленнее, злоумышленнику понадобится в тысячи раз больше времени, чтобы взломать пароль.
orip
5
Возможно, вы захотите столкновения в 128-битном пространстве от 0 до 2 ^ 128-1. Если выходное пространство алгоритма хеширования 2 ^ 128 является идеальным, то теоретически у вас просто есть подстановочный шифр с алфавитом 2 ^ 128 глифов.
jmucchiello
13
@devin - это не «мое решение», это широко распространенная практика, встроенная в стандарты криптографии на основе паролей, такие как PKCS # 5, и рекомендованная экспертами, такими как Роберт Моррис. Это чрезвычайно масштабируемо, так как доля аутентификации пользователей в легитимном приложении невелика. Масштабирование становится трудным только тогда, когда ваше приложение взламывает пароли - отсюда и рекомендация. Конечно, область поиска хэша меньше, чем у возможных паролей, но даже 128-битное пространство слишком велико для поиска методом перебора. Угроза защиты - это атака по словарю в автономном режиме.
Эриксон
6
Я имел в виду не неудобство для отдельного пользователя, а скорее стресс, который будет оказан серверу, если у вас будет большая пользовательская база, потому что вы полагаетесь на загрузку ЦП, чтобы замедлить количество запросов. Это означает, что если вы добавите больше мощности процессора, вы уменьшите ограничение для тех, кто атакует грубой силой. - Однако вы абсолютно правы в отношении масштабируемости и широко распространенной практики. Я был неправ почти во всем, что я сказал в своих предыдущих комментариях. Извините :)
DevinB
227

Для тех, кто говорит, что это безопасно, они в целом верны . «Двойное» хеширование (или его логическое расширение, повторение хеш-функции) абсолютно безопасно, если все сделано правильно , для конкретной задачи.

Тем, кто говорит, что это небезопасно, они правы в этом случае . Код, который размещен в вопросе , небезопасен. Давайте поговорим о том, почему:

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

Есть два фундаментальных свойства хеш-функции, которые нас беспокоят:

  1. Сопротивление перед изображением - учитывая хеш $h, должно быть трудно найти сообщение $m, которое$h === hash($m)

  2. Сопротивление второму изображению перед передачей - для данного сообщения $m1должно быть трудно найти другое сообщение $m2, котороеhash($m1) === hash($m2)

  3. Сопротивление столкновению - должно быть трудно найти пару сообщений, ($m1, $m2)таких, которые hash($m1) === hash($m2)(обратите внимание, что это похоже на Сопротивление второго изображения, но отличается тем, что злоумышленник может контролировать оба сообщения) ...

Для хранения паролей все, что нас действительно волнует, это Pre-Image Resistance . Два других будут спорными, потому $m1что это пароль пользователя, который мы пытаемся сохранить в безопасности. Так что, если у злоумышленника это уже есть, хешу нечего защищать ...

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ

Все, что следует, основано на предпосылке, что все, о чем мы заботимся - это Сопротивление перед изображением . Два других фундаментальных свойства хеш-функций могут (и обычно не поддерживаются) одинаково. Поэтому выводы в этом посте применимы только при использовании хеш-функций для хранения паролей. Они не применимы в общем ...

Давайте начнем

Ради этого обсуждения давайте изобретем нашу собственную хеш-функцию:

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

Теперь должно быть совершенно очевидно, что делает эта хеш-функция. Он суммирует значения ASCII каждого входного символа, а затем принимает модуль этого результата с 256.

Итак, давайте проверим это:

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

Теперь давайте посмотрим, что произойдет, если мы запустим его несколько раз вокруг функции:

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

Что выводит:

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

Хм, вау. Мы создали столкновения !!! Давайте попробуем посмотреть, почему:

Вот результат хэширования строки каждого возможного хэша:

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

Обратите внимание на тенденцию к увеличению числа. Это оказывается нашим мертвым. Запуск хеша 4 раза ($ hash = ourHash ($ hash) `для каждого элемента) приводит к получению:

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

Мы сузили себя до 8 значений ... Это плохо ... Наша оригинальная функция отображена S(∞)на S(256). То есть мы создали Сюръекцию отображение $inputв $output.

Поскольку у нас есть функция Surjective, мы не можем гарантировать, что отображение для любого подмножества входных данных не будет иметь коллизий (фактически, на практике они будут).

Вот что здесь произошло! Наша функция была плохой, но это не то, почему это работало (вот почему это работало так быстро и полностью).

То же самое происходит с MD5. Он отображается S(∞)на S(2^128). Так как нет гарантии, что работа MD5(S(output))будет Инъективной , это означает, что у нее не будет столкновений.

Секция TL / DR

Следовательно, поскольку обратная передача выходных данных md5напрямую может привести к возникновению коллизий, каждая итерация увеличивает вероятность коллизий. Однако это линейное увеличение, что означает, что хотя набор результатов 2^128уменьшается, он не уменьшается значительно быстрее, чтобы стать критическим недостатком.

Так,

$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

Чем больше раз вы повторяете, тем дальше идет сокращение.

Исправление

К счастью для нас, есть тривиальный способ исправить это: передать что-то в дальнейшие итерации:

$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    

Обратите внимание, что дальнейшие итерации не 2 ^ 128 для каждого отдельного значения для $input. Это означает, что мы можем генерировать $inputзначения, которые все еще сталкиваются по линии (и, следовательно, будут устанавливаться или резонировать при гораздо меньших, чем 2^128возможно, выходах). Но общий аргумент за $inputвсе еще так же силен, как это было для одного раунда.

Подожди, это было? Давайте проверим это с помощью нашей ourHash()функции. Переключение $hash = ourHash($input . $hash);на 100 итераций:

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

Там все еще есть грубый паттерн, но обратите внимание, что это не более паттерн, чем наша основная функция (которая уже была довольно слабой).

Заметьте, однако, что 0и 3стали коллизиями, хотя они не были в одном заезде. Это приложение из того, что я сказал ранее (что сопротивление столкновению остается неизменным для набора всех входов, но определенные маршруты столкновения могут открыться из-за недостатков в базовом алгоритме).

Секция TL / DR

Возвращая входные данные в каждую итерацию, мы эффективно устраняем любые коллизии, которые могли произойти в предыдущей итерации

Следовательно, md5($input . md5($input));должно быть ( теоретически, по крайней мере) так же сильно, как md5($input).

Это важно?

Да. Это одна из причин того, что PBKDF2 заменил PBKDF1 в RFC 2898 . Рассмотрим внутренние циклы двух:

PBKDF1:

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

Где cсчетчик итераций, Pпароль иS соль

PBKDF2:

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

Где PRF действительно просто HMAC. Но для наших целей, давайте просто скажем, что PRF(P, S) = Hash(P || S)(то есть, PRF 2 входных данных, грубо говоря, такой же, как хэш с двумя соединенными вместе). Это очень не так , но для наших целей это так.

Таким образом, PBKDF2 поддерживает сопротивление столкновения базовой Hashфункции, а PBKDF1 - нет.

Связывая все это вместе:

Мы знаем о безопасных способах итерации хэша. По факту:

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

Обычно безопасно.

Теперь, чтобы понять, почему мы хотим его хешировать, давайте проанализируем движение энтропии.

Хэш принимает бесконечное множество: S(∞)и создает меньший, постоянно измеряемый набор S(n). Следующая итерация (при условии , входа передается обратно в) картах S(∞)на S(n)вновь:

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

Обратите внимание, что конечный результат имеет точно такое же количество энтропии, что и первый . Повторение не «сделает его более затененным». Энтропия идентична. Нет волшебного источника непредсказуемости (это псевдослучайная функция, а не случайная функция).

Тем не менее, есть итерация. Это делает процесс перемешивания искусственно медленнее. И поэтому итерация может быть хорошей идеей. Фактически, это основной принцип большинства современных алгоритмов хеширования паролей (тот факт, что выполнение чего-то многократно делает это медленнее).

Замедление - это хорошо, потому что оно борется с основной угрозой безопасности: грубым принуждением. Чем медленнее мы создаем наш алгоритм хэширования, тем тяжелее атакующим приходится работать, чтобы атаковать хеши паролей, украденные у нас. И это хорошо !!!

ircmaxell
источник
1
$output = md5($output); // < 2^128 possibilities--- это действительно строгое <, или <=?
zerkms
2
@zerkms: Это не совсем что-то. Нам нужно знать некоторые очень конкретные детали базовой функции ( md5()в данном случае), чтобы действительно знать наверняка. Но в целом так будет <и не <=... Помните, речь идет о размере набора$output для всех возможных $inputs. Так что, если у нас есть еще одно столкновение будет <, следовательно <, тем лучше generalizer.
ircmaxell
2
@ TomášFejfar Я думаю, что вопрос не о коллизиях вообще, а о коллизиях в строгом наборе выходных данных (2 ^ 128 выходов, каждый ровно 128 бит шириной). Который может быть Injective, но, насколько я знаю, общее доказательство невозможно (только доказательство на примере столкновения для конкретного алгоритма). Рассмотрим хеш-функцию, которая просто возвращает входные данные, если они 128 бит (и хэширует в противном случае). В общем, это было бы сюръективно, но при подаче выходных данных оно всегда было бы инъективно ... В этом суть спора ...
ircmaxell
6
Для тех, кто хотел бы сэкономить время, не проверяя, как закончилось обсуждение между Дэном и ircmaxell, оно закончилось хорошо : Дэн согласился с ircmaxell.
Джеромей
51

Да, повторное хеширование уменьшает пространство поиска, но нет, это не имеет значения - эффективное сокращение незначительно.

Повторное хеширование увеличивает время, необходимое для грубой силы, но делать это только дважды - тоже неоптимально.

Что вам действительно нужно, так это хешировать пароль с помощью PBKDF2 - проверенного метода использования безопасного хэша с солью и итерациями. Проверьте этот SO ответ .

РЕДАКТИРОВАТЬ : Я почти забыл - не используйте MD5 !!!! Используйте современный криптографический хеш, такой как семейство SHA-2 (SHA-256, SHA-384 и SHA-512).

orip
источник
2
@DFTR - согласился. bcrypt или scrypt - лучшие варианты.
2012 г.
Не используйте их (семейство SHA-2), теперь их также можно легко взломать, проверьте crackstation.net на предмет доказательств. Во всяком случае, используйте scrypt или PBKDF2, которые являются криптографическими хеш-функциями на основе функции выведения ключей (KDF).
Теодор
3
В 2016 году каждый должен стремиться использовать Argon2 и scrypt
silkfire
10

Да - это уменьшает количество возможных строк, которые соответствуют строке.

Как вы уже упоминали, соленые хэши намного лучше.

Статья здесь: http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/ , пытается доказать, почему это эквивалентно, но я не уверен с логикой. Частично они предполагают, что не существует программного обеспечения для анализа md5 (md5 (текст)), но, очевидно, довольно просто создавать радужные таблицы.

Я все еще придерживаюсь своего ответа о том, что число хешей типа md5 (md5 (текст)) меньше, чем хешей md5 (текст), что увеличивает вероятность коллизии (даже если вероятность маловероятна) и уменьшает пространство поиска.

Рич Брэдшоу
источник
5

Большинство ответов от людей, не имеющих опыта в криптографии или безопасности. И они не правы. Используйте соль, если возможно, уникальную для каждой записи. MD5 / SHA / etc слишком быстрые, в противоположность тому, что вы хотите. PBKDF2 и bcrypt медленнее (что хорошо), но их можно победить с помощью ASIC / FPGA / GPU (в настоящее время очень доступно). Таким образом, требуется жесткий алгоритм памяти: введите scrypt .

Вот объяснение дилетанта о солях и скорости (но не об алгоритмах с жесткой памятью).

alecco
источник
4

Я просто смотрю на это с практической точки зрения. Что за хакер после? Почему, комбинация символов, которая, когда передается через хеш-функцию, генерирует желаемый хеш.

Вы сохраняете только последний хеш, поэтому хакер должен обработать только один хеш. Предполагая, что у вас примерно одинаковые шансы споткнуться о желаемом хеше с каждым шагом грубой силы, количество хешей не имеет значения. Вы могли бы сделать миллион итераций хеша, и это не увеличило бы и не уменьшило бы безопасность на один бит, так как в конце строки остается только один хеш, и шансы его разрыва такие же, как и у любого хеша.

Может быть, предыдущие постеры считают, что вклад важен; это не. Пока все, что вы вводите в хеш-функцию, генерирует желаемый хеш, оно поможет вам получить правильный ввод или неправильный ввод.

Радужные столы - это уже другая история. Поскольку радужная таблица содержит только необработанные пароли, хэширование дважды может быть хорошей мерой безопасности, поскольку радужная таблица, содержащая каждый хэш каждого хеша, будет слишком большой.

Конечно, я рассматриваю только тот пример, который дал ОП, где просто хешируется простой текстовый пароль. Если вы включите имя пользователя или соль в хеш, это другая история; хеширование дважды совершенно не нужно, поскольку радужный стол уже будет слишком большим, чтобы быть практичным и содержать правильный хеш.

Во всяком случае, не эксперт по безопасности здесь, но это только то, что я понял из моего опыта.

похлопывание
источник
Этот ответ неверен во всех отношениях. 1. Знание следующего за последним хэша не дает никакого значения злоумышленнику, потому что вход в повторный хеш - это пароль , который затем хешируется много раз (не один раз). 2. Пространство ввода - пароли, пространство вывода - хэшированные пароли. Пространство типичных паролей намного меньше, чем пространство вывода. 3. Радужные таблицы для несоленых паролей с двойным хэшированием не больше радужных таблиц для несоленных паролей с одиночным хэшированием. 4. Имена пользователей с низкой энтропией, хорошая соль - случайная. 5. Соление не заменяет итерацию. Вам нужны оба.
Клемент
3

Из того, что я прочитал, на самом деле может быть рекомендовано повторно хэшировать пароль сотни или тысячи раз.

Идея состоит в том, что, если вы можете сделать так, чтобы шифрование пароля заняло больше времени, злоумышленнику придется пройти через множество предположений, чтобы взломать пароль. Это, кажется, является преимуществом для повторного хеширования - не потому, что оно более криптографически защищено, но для генерации атаки по словарю требуется больше времени.

Конечно, компьютеры становятся все быстрее, поэтому со временем это преимущество уменьшается (или требует увеличения количества итераций).

Билл Карвин
источник
Я упоминал об этом и в другом комментарии, но en.wikipedia.org/wiki/Key_stretching
2

Лично я не стал бы беспокоиться о множественных хэшах, но я бы также обязательно хэшировал имя пользователя (или другое поле идентификатора пользователя), а также пароль, чтобы два пользователя с одинаковым паролем не имели одинаковый хеш. Кроме того, я бы, вероятно, бросил какую-то другую константную строку во входную строку тоже для хорошей меры.

$hashed_password = md5( "xxx" + "|" + user_name + "|" + plaintext_password);
CodeAndCats
источник
13
На самом деле, это должна быть строка, сгенерированная случайным образом для каждого пользователя, а не константа.
Билл Ящерица
7
Постоянный секрет работает (и с ним легче работать), если вы добавите имя пользователя, как было предложено. По сути, это создает случайный пользовательский ключ.
SquareCog
4
Постоянная тайная соль - безопасность через безвестность. Если «секрет» выясняется, что вы используете «xxx» + имя пользователя + пароль, то злоумышленнику даже не нужны данные из ваших таблиц, чтобы начать атаку против него.
Билл Ящерица
8
Я не думаю, что это безопасность через неизвестность. Причина использования соли в том, что вы не можете вычислить радужную таблицу для нескольких хешей md5 одновременно. Создание одного для «ххх» + пароль (та же соль) происходит один раз. Создание таблицы для «ххх» + имя пользователя + пароль хуже, чем перебор.
FryGuy
5
@Bill the Lizard: «атака сводится к созданию одного словаря для атаки на конкретное имя пользователя» - это просто атака грубой силой (на самом деле это еще хуже, потому что помимо вычисления всех хеш-кодов их нужно хранить), так что соль работает отлично в этом случае.
Корнель
2

Предположим, вы используете алгоритм хеширования: вычислите rot13, возьмите первые 10 символов. Если вы сделаете это дважды (или даже 2000 раз), можно создать функцию, которая работает быстрее, но дает тот же результат (а именно, просто взять первые 10 символов).

Точно так же можно создать более быструю функцию, которая дает тот же результат, что и повторяющаяся функция хеширования. Поэтому ваш выбор функции хеширования очень важен: как и в примере с rot13, не учитывается, что повторное хеширование повысит безопасность. Если нет исследований о том, что алгоритм предназначен для рекурсивного использования, то безопаснее предположить, что он не даст вам дополнительной защиты.

Тем не менее, для всех, кроме самых простых функций хеширования, скорее всего, потребуются эксперты по криптографии для вычисления более быстрых функций, поэтому, если вы защищаетесь от злоумышленников, не имеющих доступа к специалистам по криптографии, на практике, вероятно, безопаснее использовать функцию повторного хеширования ,

Оле Танге
источник
1

Как правило, он не обеспечивает дополнительной безопасности для двойного хеширования или двойного шифрования чего-либо. Если вы можете разорвать хэш один раз, вы можете разорвать его снова. Обычно это не наносит вреда безопасности.

В вашем примере использования MD5, как вы, вероятно, знаете, есть некоторые проблемы коллизий. «Двойное хеширование» на самом деле не помогает защитить от этого, так как те же коллизии по-прежнему приводят к тому же первому хешу, который вы затем можете снова использовать MD5 для получения второго хеша.

Это защищает от атак по словарю, таких как «обратные базы данных MD5», но также и от соления.

С одной стороны, двойное шифрование чего-либо не обеспечивает никакой дополнительной безопасности, потому что все, что он делает, это приводит к другому ключу, который является комбинацией двух фактически используемых ключей. Таким образом, усилия по поиску «ключа» не удваиваются, потому что два ключа на самом деле не нужно искать. Это не верно для хеширования, потому что результат хеша обычно не совпадает с длиной исходного ввода.

импровизированная трибуна
источник
1
Все правильно, но я просто хочу заметить, что влияние компромисса с сильным сопротивлением столкновению на MD5 слегка нарушено - большинство сценариев, использующих крипто-хэш-функции, не полагаются на сильное сопротивление столкновению, просто слабое сопротивление. Они не подвержены этой уязвимости.
SquareCog
1

Двойное хеширование имеет смысл для меня, только если я хеширую пароль на клиенте, а затем сохраняю хеш (с другой солью) этого хеша на сервере.

Таким образом, даже если кто-то взломал его путь к серверу (игнорируя тем самым безопасность SSL), он все равно не сможет получить чистые пароли.

Да, у него будут данные, необходимые для проникновения в систему, но он не сможет использовать эти данные для компрометации внешних учетных записей, которые есть у пользователя. И люди, как известно, используют один и тот же пароль практически для чего угодно.

Единственный способ получить чистые пароли - это установить кейген на клиенте - и это больше не ваша проблема.

Итак, вкратце:

  1. Первое хэширование на клиенте защищает ваших пользователей в случае «взлома сервера».
  2. Второе хеширование на сервере служит для защиты вашей системы, если кто-то захватил резервную копию вашей базы данных, поэтому он не может использовать эти пароли для подключения к вашим службам.
Ведран
источник
1
+1 Я ждал, чтобы увидеть ответ, подобный этому, потому что я думал о том же сценарии, когда вы не хотите хранить открытый текстовый пароль на клиенте, но также не отправляете окончательный зашифрованный пароль по проводам, чтобы сделать простое сравнение с БД.
Марк
1
Не помогает для веб-приложений. если ваш сервер скомпрометирован, то код, который ваш сервер отправляет клиенту, также скомпрометирован. Атакующий отключит ваш хэш на стороне клиента и получит необработанные пароли.
Клемент
0

Забота об уменьшении пространства поиска является математически правильной, хотя пространство поиска остается достаточно большим, что для всех практических целей (при условии, что вы используете соли), при 2 ^ 128. Тем не менее, поскольку мы говорим о паролях, число возможных 16-символьных строк (буквенно-цифровые, заглавные буквы, добавленные несколько символов) составляет примерно 2 ^ 98, согласно моим подсчетам за пределами конверта. Таким образом, воспринимаемое уменьшение в пространстве поиска не очень актуально.

Помимо этого, на самом деле нет никакой разницы, если говорить криптографически.

Хотя существует криптографический примитив, называемый «цепочкой хеширования» - методика, которая позволяет вам делать некоторые интересные трюки, такие как раскрытие ключа подписи после его использования, без ущерба для целостности системы - учитывая минимальное время синхронизации, это позволяет чисто обойти проблему первоначального распределения ключей. По сути, вы предварительно вычисляете большой набор хэшей хэшей - h (h (h (h .... (h (k)) ...))), используйте n-ное значение для подписи, после установленного интервала вы отправляете ключ и подпишите его, используя ключ (n-1). Получатели могут теперь проверить, что вы отправили все предыдущие сообщения, и никто не может подделать вашу подпись, так как прошел период времени, в течение которого она действительна.

Повторное хэширование сотен тысяч раз, как предлагает Билл, - просто пустая трата вашего процессора. Используйте более длинный ключ, если вы беспокоитесь о людях, ломающих 128 бит.

SquareCog
источник
1
Повторное хеширование - это как раз замедление хеширования. Это ключевая функция безопасности в криптографии на основе паролей. Смотрите ссылки для PCKS5 и PBKDF2.
orip
0

Как показывают несколько ответов в этой статье, в некоторых случаях это может повысить безопасность, а в других - причинить вред. Есть лучшее решение, которое определенно улучшит безопасность. Вместо того, чтобы удваивать количество раз, которое вы вычисляете хеш, удваивайте размер вашей соли, или удваивайте количество бит, используемых в хэше, или делайте оба! Вместо SHA-245, прыгайте до SHA-512.

Стефан Русек
источник
Это не отвечает на вопрос.
Билл Ящерица
1
Двойное хеширование не стоит затраченных усилий, но удвоение размера хеша - это. Я думаю, что это более ценный момент.
Стефан Русек
-1

Двойное хэширование ужасно, потому что, скорее всего, злоумышленник построил таблицу, чтобы получить большинство хэшей. Лучше солить свои хеши и смешивать их вместе. Существуют также новые схемы для «подписывания» хешей (в основном, соления), но более безопасным способом.

Саргун Диллон
источник
-1

Да.

Абсолютно не используйте несколько итераций обычной хеш-функции, например md5(md5(md5(password))). В лучшем случае вы получите незначительное повышение безопасности (подобная схема практически не защищает от атак на GPU; просто конвейерная обработка). В худшем случае вы уменьшаете свое хэш-пространство (и, следовательно, безопасность) с каждой добавляемой итерацией , В безопасности разумно предположить худшее.

Есть ли использовать пароль , который был разработан компетентным криптограф , чтобы быть эффективным хэш пароля, а также устойчив как к грубой силы и времени космического нападения. К ним относятся bcrypt, scrypt и, в некоторых случаях, PBKDF2. Хеш на основе glibc SHA-256 также приемлем.

Hobbs
источник
-1

Я собираюсь выйти на конечность и сказать, что в определенных обстоятельствах она более безопасна ... но пока не понижайте меня!

С математической / криптографической точки зрения это менее безопасно по причинам, которые, я уверен, кто-то другой даст вам более четкое объяснение, чем я мог бы.

тем не мение существуют большие базы данных хэшей MD5, которые с большей вероятностью содержат текст «пароля», чем его MD5. Таким образом, двойное хеширование снижает эффективность этих баз данных.

Конечно, если вы используете соль, то это преимущество (недостаток?) Исчезнет.

Greg
источник