Чи "подвійне хешування" пароль менш безпечний, ніж просто хешування його один раз?


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 місяців. Наприклад, щоб перемогти більш складного зловмисника - того, хто здатний завантажити програму, яка може використовувати потужність свого GPU - наприклад, потрібно більше ітерацій.

Скільки вистачає?

Кількість повторень, які слід використовувати, є компромісом між безпекою та користувачем. Спеціалізоване обладнання, яким можуть скористатися зловмисники, є дешевим, але воно все одно може виконувати сотні мільйонів ітерацій в секунду. Продуктивність системи зловмисника визначає, скільки часу потрібно зламати пароль, враховуючи ряд ітерацій. Але ваша програма, швидше за все, не використовує це спеціалізоване обладнання. Скільки ітерацій можна виконувати, не обтяжуючи користувачів, залежить від вашої системи.

Можливо, ви можете дозволити користувачам чекати додаткову ¾ секунду або близько того під час аутентифікації. Профілюйте свою цільову платформу та використовуйте стільки ітерацій, скільки ви можете собі дозволити. Тестовані платформи (один користувач на мобільному пристрої або багато користувачів на серверній платформі) можуть зручно підтримувати PBKDF2 з 60 000 до 120 000 ітерацій, або bcrypt з коефіцієнтом вартості 12 або 13.

Більше тла

Прочитайте ПКС №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. Попереднє зображення Resistance - З огляду на хеш $h, це повинно бути важко знайти повідомлення $mтаке , що$h === hash($m)

  2. Опір другого перед зображенням - з огляду на повідомлення $m1, важко знайти інше $m2таке повідомленняhash($m1) === hash($m2)

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

Для зберігання паролів все, що нас дійсно дбає, - це попередній опір зображення . Інші два будуть суперечливими, оскільки $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 рази ($ хеш = наш Хеш ($ хеш) `, для кожного елемента) закінчується, даючи нам:

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)

Зауважте, що кінцевий результат має точно таку ж кількість ентропії, як і перший . Ітерація не "зробить її більш затьмареною". Ентропія ідентична. Немає магічного джерела непередбачуваності (це псевдовипадкова функція, а не випадкова функція).

Однак є користь від ітерації. Це робить процес хешування штучно повільніше. І тому повторення може бути хорошою ідеєю. Насправді, це основний принцип більшості сучасних алгоритмів хешування паролів (той факт, що робити щось надмірно, робить це повільніше).

Повільне - це добре, бо бореться з первинною загрозою безпеці: жорстоким форсуванням. Чим повільніше ми робимо наш алгоритм хешування, тим складніше зловмисникам доводиться працювати над атакою вкрадених у нас хешей паролів. І це гарна річ !!!


1
$output = md5($output); // < 2^128 possibilities--- це дійсно суворо <, чи <=?
zerkms

2
@zerkms: Це не зовсім. Нам потрібно було б знати деякі дуже конкретні деталі основної функції ( md5()в даному випадку), щоб дійсно знати точно. Але загалом це буде <і не <=... Пам'ятайте, ми говоримо про розмір набору $outputдля всього можливого $inputs. Тож якщо ми маємо навіть одне зіткнення, воно буде <, тому <кращий генератор.
ircmaxell

2
@ TomášFejfar Я думаю, що питання не в зіткненні взагалі, а в зіткненнях у суворому наборі виходів (2 ^ 128 виходів, кожен рівно 128 біт шириною). Це може бути ін'єктивним, але, наскільки я знаю, загальне доведення неможливо (лише доказ прикладу зіткнення для конкретного алгоритму). Розглянемо хеш-функцію, яка просто повертає вхід, якщо він становить 128 біт (а хеширується в іншому випадку). Взагалі це було б сюржективно, але коли годувати його вихід, воно завжди було б ін'єктивним ... У цьому суперечка ...
ircmaxell


6
Для тих, хто хотів би заощадити час, не потрібно їхати перевірити, як закінчилася ця дискусія між Dan & ircmaxell, чи закінчилася вона добре : Ден погодився з ircmaxell.
jeromej

51

Так, повторне хешування зменшує простір пошуку, але ні, це не має значення - ефективне зменшення є незначним.

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

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

EDIT : Я майже забув - НЕ ВИКОРИСТУЙТЕ MD5 !!!! Використовуйте сучасний криптографічний хеш, такий як сімейство SHA-2 (SHA-256, SHA-384 та SHA-512).


2
@DFTR - погодився. bcrypt або scrypt - кращі варіанти.
Оріп

Не використовуйте жодних з них (сімейство 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 / тощо занадто швидко, навпаки тому, що ви хочете. PBKDF2 і bcrypt повільніші (що добре), але їх можна перемогти за допомогою ASIC / FPGA / GPU (сьогодні дуже доступно). Тож потрібен алгоритм, що є важким для пам'яті: введіть скрипт .

Ось роз'яснення мирян щодо солей та швидкості (але не про алгоритми, важкі для пам'яті).


4

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

Ви зберігаєте лише останній хеш, отже, хакер повинен лише забити один хеш. Якщо припустити, що у вас є однакові шанси наткнутися на потрібний хеш з кожним кроком грубої сили, кількість хешей не має значення. Ви можете зробити мільйон хеш-ітерацій, і це не збільшить і не знизить безпеку ні на один біт, оскільки в кінці рядка є ще один хеш-лайк, і шанси на його розбивку такі ж, як у будь-якого хеша.

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

Тепер веселкові столи - це вже інша історія. Оскільки таблиця веселки містить лише необроблені паролі, хешування два рази може бути хорошим заходом безпеки, оскільки таблиця веселок, яка містить кожен хеш кожного хешу, була б занадто великою.

Звичайно, я розглядаю лише той приклад, який дав ОП, де це просто хеш-код простого тексту. Якщо ви включите ім'я користувача або сіль в хеш, це вже інша історія; двічі хешування - зовсім непотрібне, оскільки райдужний стіл уже був би занадто великим, щоб бути практичним і містив би правильний хеш.

У всякому разі, тут не експерт з питань безпеки, але це саме те, що я зрозумів зі свого досвіду.


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

3

З того, що я прочитав, насправді може бути рекомендовано повторно хешувати пароль сотні чи тисячі разів.

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

Звичайно, комп'ютери стають все швидшими, тому ця перевага з часом зменшується (або вимагає від вас збільшувати ітерації).


Про це я згадав і в іншому коментарі, але en.wikipedia.org/wiki/Key_stretching

2

Особисто я б не зациклювався на декількох хешах, але я б переконався, що також хешувати ім'я користувача (або інше поле ідентифікатора користувача), а також пароль, щоб два користувачі з одним паролем не опинилися з одним і тим же хешем. Крім того, я б, мабуть, кинув якусь іншу постійну рядок у рядок введення для гарної міри.

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

13
Насправді це має бути рядок, що генерується випадковим чином для кожного користувача, а не константа.
Білл Ящірка

7
Постійний секрет працює (і з ним легше працювати), якщо ви вкажете ім'я користувача, як було запропоновано. Це по суті виробляє випадковий користувальницький ключ.
SquareCog

4
Постійна таємна сіль - це безпека через неясність. Якщо "секрет" виявиться, що ви використовуєте "xxx" + ім'я користувача + пароль, то зловмиснику навіть не потрібні дані з ваших таблиць, щоб розпочати атаку проти нього.
Білл Ящірка

8
Я не думаю, що це безпека через неясність. Причиною використання солі є те, що ви не можете обчислити таблицю веселки проти кількох хедів md5 одночасно. Створення одного для "xxx" + пароля (тієї ж солі) відбувається один раз. Створення таблиці для "xxx" + ім'я користувача + пароль гірше, ніж грубе форсування.
FryGuy

5
@Bill 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 біт.


1
Повторне хешування полягає саме в уповільненні хешу. Це ключова функція безпеки в криптографії на основі пароля. Дивіться посилання для PCKS5 та PBKDF2.
orip

0

Як свідчить декілька відповідей у ​​цій статті, є деякі випадки, коли це може покращити безпеку, та інші, коли це напевно шкодить. Є краще рішення, яке напевно покращить безпеку. Замість того, щоб подвоювати кількість разів, коли ви обчислюєте хеш, подвоюйте розмір солі або подвоюйте кількість використаних бітів у хеші або виконайте обидва! Замість SHA-245 підскочіть до SHA-512.


Це не відповідає на запитання.
Білл Ящірка

1
Подвійне хешування не варто докладати зусиль, але подвоєння розміру хешу є. Я думаю, що це є більш цінним моментом.
Стефан Русек

-1

Подвійне хешування є некрасивим, тому що більш ніж ймовірно, зловмисник створив таблицю для створення більшості хешей. Краще посолити хеши і перемішати хеші разом. Існують також нові схеми "підписання" хешей (в основному засолювання), але більш безпечним чином.


-1

Так.

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

Є чи використовувати пароль , який був розроблений компетентним криптограф , щоб бути ефективним хеш пароля, а також стійкий як до грубої сили і часу космічного нападу. До них відносяться bcrypt, scrypt та в деяких ситуаціях PBKDF2. Хеш на основі glibc SHA-256 також прийнятний.


-1

Я збираюся вийти на кінцівку і сказати, що це безпечніше в певних обставинах ... все-таки не прихиляй мене!

З математичної / криптографічної точки зору, це менш безпечно, з тих причин, що я впевнений, що хтось інший дасть вам чіткіше пояснення, ніж я міг би.

Однак існують великі бази даних хешей MD5, які, швидше за все, містять текст "пароля", ніж його MD5. Таким чином, подвійним хешированием ви знижуєте ефективність цих баз даних.

Звичайно, якщо ви використовуєте сіль, то ця перевага (недолік?) Відпадає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.