Процес хешування (на відміну від шифрування) призводить до того, що на виході з вхідних даних отримується такий рядок, з якого вже неможливо відновити вихідний рядок.
Тому він добре підходить для захисту конфіденційних рядків, паролів і контрольних сум.
Ще однією приємною особливістю функцій хешування є те, що вони завжди генерують вихідні дані однакової довжини, а невелика зміна на вході завжди повністю змінює весь вихід.
У PHP є багато хеш-функцій, ось найважливіші з них:
$password = 'секретний пароль';echo password_hash($password); // Bcryptecho md5($password);echo sha1($password);
Попередження:** Ані
md5()
, аніsha1()
не підходять для хешування паролів, оскільки за їх допомогою легко розкрити оригінальний пароль або, принаймні, попередньо обчислити паролі. Набагато краще використовуватиbcrypt
, який був розроблений для хешування паролів.На сайті md5cracker.com є база даних контрольних сум (хешів), спробуйте пошукати хеш:
79c2b46ce2594ecbcb5b73e928345492
, як бачите, такий чистийmd5()
не настільки безпечний для звичайних слів і паролів.
У доповіді Як не помилитися в цільовій площині Девід Градл розповів про те, як правильно хешувати та зберігати паролі.
Єдине правильне рішення: "Шифр + сіль".
А саме:
$password = 'хеш';// Генерує безпечний хешecho password_hash($password, PASSWORD_BCRYPT);// Альтернативний варіант з більшою складністю (за замовчуванням 10):echo password_hash($password, PASSWORD_BCRYPT, ['вартість' => 12]);
Перевага Bcryp полягає, головним чином, у швидкості та автоматичному посолі.
Той факт, що для генерації, скажімо, 100 мс потрібно довго, робить тестування багатьох паролів дуже дорогим для зловмисника.
Крім того, вихідний хеш автоматично обробляється випадковою сіллю, що означає, що при багаторазовому хешуванні одного і того ж пароля на виході завжди виходить різний хеш. Таким чином, зловмисник не зможе використати заздалегідь розраховану хеш-таблицю.
Тому ми не зможемо перевірити правильність пароля шляхом багаторазового хешування, а будемо змушені викликати спеціалізовану функцію:
if (password_verify($password, $hash)) {// Пароль правильний} else {// Неправильний пароль}
Для того, щоб ускладнити злом хешу, доцільно вставити додатковий рядок у вихідні дані. В ідеалі - випадковий. Цей процес називається засолювання паролем.
Безпека базується на тому, що зловмисник не зможе скористатися заздалегідь розрахованою таблицею паролів та хешів, оскільки не знатиме "солі" і буде змушений зламувати паролі індивідуально.
Наприклад:
$password = 'секретний_паспорт';$salt = 'fghjgtzjhg';$hash = md5($password . $salt);echo $password; // виводить оригінальний парольecho $hash; // виводить хеш паролю з урахуванням солі
Ви можете подумати, що було б непогано виконувати функцію хешування багаторазово, тим самим підвищуючи складність злому, оскільки оригінальний пароль потрібно буде хешувати повторно.
Наприклад:
$password = 'пароль';for ($i = 0; $i <= 1000; $i++) {$password = md5($password);}echo $password; // 1000x хешування через md5()
Парадоксально, але складність прориву зменшується або залишається майже незмінною.
Причина в тому, що функція md5()
надзвичайно швидка і на звичайному комп'ютері може обчислити понад мільйон хешів за секунду, тому перебір паролів по черзі не сильно сповільнює роботу.
Друга причина - це більше теорія, а саме можливість наштовхнутися на так зване зіткнення. Якщо ми хешуємо пароль багаторазово, то з часом може статися так, що ми потрапимо на хеш, який зловмисник вже знає, і це дозволить йому хешувати пароль, використовуючи базу даних.
Тому краще використовувати функцію повільного безпечного хешування і виконувати хешування тільки один раз, при цьому все одно обробляючи кінцевий результат засолюванням.
Чи знаєте ви, що оператор === не є найбезпечнішим вибором для порівняння хешів при перевірці паролів?
При порівнянні рядків він перебирає два рядки символ за символом до тих пір, поки не дійде до кінця (успіх, вони однакові) або не знайде різниці (рядки різні).
І це може бути використано в атаці. Якщо виміряти час досить точно, то можна оцінити, скільки ще символів залишилося додати, щоб отримати точний збіг і дійти до кінця, або можна оцінити, наскільки далеко пройшли рядки при порівнянні рядків.
Рішення полягає у використанні функції hash_equals() скрізь, де порівнюються рядки, і було б важливо, якби зловмисник міг дізнатися позицію, де порівняння не відбулося.
І як функція це робить? Він гарантує, що порівняння будь-яких 2 струн завжди займає однакову кількість часу, так що ви не можете сказати, вимірявши час, де сталася різниця. Я вважаю, що деякі види атак дійсно дуже малоймовірні і їх важко реалізувати. Це одна з них.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | uk