Як хешувати довгі паролі (> 72 символи) за допомогою blowfish


91

Останній тиждень я прочитав багато статей про хешування паролів, і Blowfish, здається, є (одним з) найкращих алгоритмів хешування на даний момент - але це не тема цього питання!

Обмеження 72 символів

Blowfish враховує лише перші 72 символи введеного пароля:

<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);

$input = substr($password, 0, 72);
var_dump($input);

var_dump(password_verify($input, $hash));
?>

Вихід:

string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)

Як бачите, значення мають лише перші 72 символи. Twitter використовує blowfish, який називається bcrypt, для зберігання своїх паролів ( https://shouldichangemypassword.com/twitter-hacked.php ) і вгадайте, що: змініть свій пароль на Twitter на довгий пароль із більш ніж 72 символами, і ви зможете увійти до свого облікового запису, введення лише перших 72 символів.

Видувка та перець

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

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

Тож я думаю, що розписування пароля - це принаймні не поганий вибір.

Пропозиція

Моя пропозиція отримати хеш Blowfish для пароля, який має більше 72 символів (і перцю), це:

<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";

// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);

// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);

var_dump(password_verify($input_peppered, $hash));
?>

Це базується на цьому питанні : password_verifyповернення false.

Питання

Який безпечніший спосіб? Спочатку отримуєте хеш SHA-256 (який повертає 64 символи) або враховуєте лише перші 72 символи пароля?

Плюси

  • Користувач не може увійти, ввівши лише перші 72 символи
  • Ви можете додати перець, не перевищуючи обмеження кількості символів
  • Результат hash_hmac мабуть мав би більше ентропії, ніж сам пароль
  • Пароль хешується двома різними функціями

Мінуси

  • Лише 64 символи використовуються для побудови хешу blowfish


Редагування 1: Це питання стосується лише інтеграції PHP в blowfish / bcrypt. Дякуємо за коментарі!


3
Blowfish не єдиний, хто скорочує пароль, вводить людей в оману, вважаючи, що це безпечніше, ніж це насправді. Ось цікава історія обмеження 8 символів.
DOK

2
Чи є 72-символьне скорочення фундаментальним для алгоритму Blowfish чи просто реалізація PHP? IIRC Blowfish також використовується на (принаймні деяких) ніксах для шифрування паролів користувачів.
Дуглас Б. Степл

3
Справа в Bcrypt, а не в Blowfish. Я можу відтворити цю проблему лише за допомогою Python та Bcrypt.
Blender

@Blender: Дякуємо за ваш коментар та вашу роботу над ним. Я не зміг знайти різні функції в php для blowfish та bcrypt, і хоча вони однакові. Але хіба це не має для мене ніякої різниці у php? Я вважаю за краще використовувати стандартну функцію php.
Фредерік Каммер,

1
Також дивіться фреймворк PHP для OpenHall (PHPass). Він портативний і загартований проти ряду поширених атак на паролі користувачів. Хлопець, який написав фреймворк (SolarDesigner), - це той самий хлопець, який написав Джона Різника і є суддею у Конкурсі на хешування паролів . Тож він знає дещо про напади на паролі.
jww

Відповіді:


135

Проблема тут полягає в основному в проблемі ентропії. Тож давайте почнемо шукати там:

Ентропія на персонажа

Кількість бітів ентропії на байт:

  • Шестигранні символи
    • Біти: 4
    • Цінності: 16
    • Ентропія за 72 символи: 288 біт
  • Буквено-числові
    • Біти: 6
    • Значення: 62
    • Ентропія за 72 символи: 432 біти
  • "Загальні" символи
    • Біти: 6.5
    • Цінності: 94
    • Ентропія за 72 символи: 468 біт
  • Повні байти
    • Біти: 8
    • Значення: 255
    • Ентропія за 72 символи: 576 біт

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

Перша проблема

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

Таким чином, перемішуючи ваш перець, ви фактично скорочуєте максимальну ентропію, доступну для пароля, у 2 рази (з 576 до 288 можливих бітів).

Друга проблема

Однак, насамперед sha256передбачає лише 256біти ентропії. Отже, ви ефективно скорочуєте можливі 576 біт до 256 біт. Ваш хеш-крок * негайно * за самим визначенням втрачає щонайменше 50% можливої ентропії в паролі.

Ви можете частково вирішити цю проблему, переключившись на SHA512, де ви лише зменшите доступну ентропію приблизно на 12%. Але це все-таки не несуттєва різниця. Ці 12% зменшують кількість перестановок у коефіцієнт 1.8e19. Це велике число ... І це фактор, який зменшує його на ...

Основна проблема

Основна проблема полягає в тому, що існує три типи паролів довжиною до 72 символів. Вплив цієї системи стилів на них буде дуже різним:

Примітка: з цього моменту я припускаю, що ми порівнюємо з системою перцю, яка використовує SHA512вихідний вихід (не шістнадцятковий).

  • Високі ентропійні випадкові паролі

    Це ваші користувачі, які використовують генератори паролів, які генерують величину великих ключів для паролів. Вони випадкові (генеровані, а не вибрані людиною) і мають високу ентропію на персонажа. Ці типи використовують великі байти (символи> 127) та деякі контрольні символи.

    Для цієї групи ваша функція хешування значно зменшить їх доступну ентропію bcrypt.

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

  • Випадкові паролі середньої ентропії

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

    Для цієї групи ви збираєтеся трохи розблокувати більше ентропії (не створювати її, але дозволити більшій ентропії поміститися в паролі bcrypt). Коли я кажу трохи, я маю на увазі трохи. Беззбитковість виникає, коли ви перевищуєте 512 біт, які має SHA512. Отже, пік становить 78 символів.

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

  • Низькі ентропійні невипадкові паролі

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

    Для цієї групи ви можете значно розблокувати більше ентропії (не створювати її, але дозволити більшій кількості вміщуватися у введення пароля bcrypt) шляхом хешування. Беззбитковість становить близько 223 символів, перш ніж ви закінчите ентропію.

    Скажімо ще раз. Для цього класу паролів попереднє хешування безумовно значно підвищує рівень безпеки.

Назад до реального світу

Такі види обчислення ентропії насправді не мають великого значення в реальному світі. Важливим є вгадування ентропії. Це те, що безпосередньо впливає на те, що можуть зробити зловмисники. Це те, що ви хочете максимізувати.

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

Шанси випадково вгадати 72 правильних символи поспіль надзвичайно низькі. Швидше за все виграєте у лотерею Powerball 21 раз, ніж у цьому зіткненні ... Ось про яке велике число ми говоримо.

Але ми не можемо натрапити на це статистично. У випадку фраз шанс перших 72 символів бути однаковими набагато вищий, ніж для випадкового пароля. Але він все ще тривіально низький (ви, швидше за все, виграєте в лотерею Powerball 5 разів, виходячи з 2,3 біта на персонажа).

Практично

Практично це не має значення. Шанси когось вгадати перші 72 символи правильно, де останні мають суттєву різницю, настільки низькі, що не варто турбуватися. Чому?

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

Візьмемо приклад. Візьмемо цитату з Біблії (просто тому, що це поширене джерело довгого тексту, а не з будь-якої іншої причини):

Не бажай дому свого ближнього. Не бажай дружини свого сусіда, ані його невільниці, ані невільниці, ані вола, ані осла, ані чогось, що належить сусідові.

Це 180 символів. 73-й символ - gдругий neighbor's. Якщо ви так багато здогадалися, ви, швидше за все, не зупиняєтесь nei, а продовжуєте з рештою вірша (оскільки саме так, швидше за все, буде використовуватися пароль). Тому ваш "хеш" не додав багато.

До речі: Я АБСОЛЮТНО НЕ виступаю за використання біблійних цитат. Насправді, якраз навпаки.

Висновок

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

Але зрештою, жодне з них не є надмірно значущим. Цифри, з якими ми маємо справу, просто НАДАЛЬКО високі. Різниця в ентропії не буде великою.

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

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

Це мої $ 0,02 принаймні (або, можливо, більше, ніж 0,02 $) ...

Щодо використання "таємного" перцю:

Буквально немає досліджень щодо введення однієї хеш-функції в bcrypt. Отже, незрозуміло в кращому випадку, якщо додавання "перфорованого" хешу в bcrypt коли-небудь спричинить невідомі вразливості (ми знаємо, що це hash1(hash2($value))може виявити значні вразливості навколо стійкості до зіткнень та атак перед зображеннями).

Враховуючи, що ви вже розглядаєте можливість зберігати секретний ключ ("перець"), чому б не використовувати його добре вивченим та зрозумілим способом? Чому б не зашифрувати хеш перед його зберіганням?

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

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

Примітка: якщо ви вирішите це зробити, скористайтеся бібліотекою. Для PHP я настійно рекомендую Zend\Cryptпакет Zend Framework 2 . Насправді це єдиний, який я б рекомендував на даний момент. Це було ретельно переглянуто, і воно приймає всі рішення за вас (що дуже добре) ...

Щось на зразок:

use Zend\Crypt\BlockCipher;

public function createHash($password) {
    $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);

    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    return $blockCipher->encrypt($hash);
}

public function verifyHash($password, $hash) {
    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    $hash = $blockCipher->decrypt($hash);

    return password_verify($password, $hash);
}

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

Будь-яка людина, від самого невідомого аматора до найкращого криптографа, може створити алгоритм, який він сам не може зламати.


6
Щиро дякую за цю детальну відповідь. Це справді мені допомагає!
Фредерік Каммер

1
Мій комплімент за цю відповідь. Хоча один маленький вибір, це переважна більшість користувачів, які використовують дуже слабкі паролі, слова та похідні, що містяться у словнику для злому паролів, перець захистить їх незалежно від питань ентрофії. Щоб уникнути втрати ентрофії, ви можете просто об’єднати пароль і перець. Однак ваша пропозиція щодо шифрування хеш-значення є, мабуть, найкращим рішенням для додавання секрету на стороні сервера.
martinstoeckli

2
@martinstoeckli: Моє питання з поняттям перцю не в його цінності. Саме в тому, що застосування "перцю" переходить на невизначену територію з точки зору криптографічних алгоритмів. Це не дуже добре. Натомість я вважаю, що криптографічні примітиви слід поєднувати таким чином, щоб вони були розроблені для поєднання. В основному, основна концепція перцю звучить мені у вухах, як деякі люди, які нічого не знали про криптографію, сказали: "Більше хешів - це краще! У нас є сіль, перець теж хороший!" . Я б просто
віддав

@ircmaxell - Так, я знаю вашу точку зору, і я погоджуюсь, доки хеш-значення потім будуть зашифровані. Якщо ви не зробите цей додатковий крок, атака за словником просто виявить занадто багато слабких паролів, навіть із хорошим алгоритмом хешування.
martinstoeckli

@martinstoeckli: Я з цим не згоден. Зберігання таємниць - справа нетривіальна. Натомість, якщо ви використовуєте bcrypt з хорошою вартістю (сьогодні 12), усі, крім найслабшого класу паролів, безпечні (словник, а тривіальні паролі - слабкі). Тому я б скоріше рекомендував людям зосередитись на навчанні користувача з використанням вимірювачів міцності та
спонуканні

5

Додавання паролів, безумовно, непогано, але давайте подивимося, чому.

Спочатку слід відповісти на питання, коли саме перець допомагає. Перець захищає лише паролі, поки вони залишаються в секреті, тому, якщо зловмисник має доступ до самого сервера, він не приносить користі. Набагато простіша атака - це SQL-ін'єкція, яка дозволяє отримати доступ для читання до бази даних (до наших хеш-значень). Я підготував демонстраційну версію SQL-ін'єкції, щоб показати, наскільки легко це може бути (натисніть наступну стрілку, щоб отримати введення).

Тоді від чого насправді допомагає перець? Поки перець залишається в таємниці, він захищає слабкі паролі від нападу на словник. Тоді пароль 1234став би приблизно таким 1234-p*deDIUZeRweretWy+.O. Цей пароль не тільки набагато довший, він також містить спеціальні символи і ніколи не буде частиною жодного словника.

Тепер ми можемо підрахувати, якими паролями користуватимуться наші користувачі, ймовірно, більше користувачів буде вводити слабкі паролі, оскільки є користувачі з паролями між 64-72 символами (насправді це буде дуже рідко).

Інший момент - діапазон для примушення грубих. Хеш-функція sha256 поверне 256 біт на виході або комбінації 1.2E77, це занадто багато для грубого примусу, навіть для графічних процесорів (якщо я правильно розрахував, для цього потрібно близько 2E61 років на графічному процесорі в 2013 році). Тому ми не отримуємо реального недоліку, застосовуючи перець. Оскільки хеш-значення не є систематичними, ви не можете пришвидшити грубу силу з загальними шаблонами.

PS Наскільки мені відомо, обмеження в 72 символи є специфічним для самого алгоритму BCrypt. Найкраща відповідь, яку я знайшов, це така .

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


Щодо вашого PPS, я можу просто сказати: Так, він може перевірити усічений пароль за допомогою хешу ненарізаного і все одно отримати true. Ось про що це питання. Подивіться самі: viper-7.com/RLKFnB
Sliq

@Panique - Проблема не в обчисленні хешу BCrypt, а в HMAC раніше. Для генерації хешу SHA OP використовує пароль повної довжини і використовує результат як вхід для BCrypt. Для перевірки він усікає пароль перед обчисленням хешу SHA, а потім використовує цей зовсім інший результат як вхід для BCrypt. HMAC приймає введення будь-якої довжини.
martinstoeckli

2

Bcrypt використовує алгоритм, заснований на дорогому алгоритмі налаштування ключа Blowfish.

Рекомендоване обмеження пароля 56 байт (включаючи нульовий байт закінчення) для bcrypt стосується обмеження 448 бітів ключа Blowfish. Будь-які байти, що перевищують цю межу, не повністю змішуються в отриманому хеші. Тому абсолютний ліміт 72 байтів для паролів bcrypt є менш актуальним, якщо врахувати фактичний вплив цих байтів на результуючий хеш.

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

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

Якщо ви все-таки вирішите попередньо хешувати пароль, який перевищує 55 байт, вам слід використовувати SHA-384, оскільки він має найбільший вихід, не перевищуючи обмеження.


1
"термін дії пароля також повинен бути коротким, як 2 тижні" "дуже довгого пароля", дійсно, навіщо тоді навіть зберігати пароль, просто використовуйте скидання пароля щоразу. Серйозно, це неправильне рішення, перейдіть до двофакторної автентифікації за допомогою маркера.
zaph

Дякую @zaph. Чи можете ви вказати мені на приклад цього? Це звучить цікаво.
Філ

[ПРОЕКТ NIST Спеціальна публікація 800-63B Керівництво з цифрової автентифікації] ( pages.nist.gov/800-63-3/sp800-63b.html ), 5.1.1.2. Запам’ятані секретні верифікатори: верифікатори НЕ ПОВИННІ вимагати довільної зміни запам'ятовуваних секретів (наприклад, періодично) . Також див. Розділ «Покращення вимог до пароля» Джима Фентона.
zaph

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