Що ж, ти можеш шукати це у Вікіпедії ... Але оскільки ти хочеш пояснення, я тут все зроблю:
Хеш-функції
Вони забезпечують відображення між входом довільної довжини та вихідним (як правило) фіксованою довжиною (або меншою довжиною). Це може бути що завгодно, від простого crc32, до повноцінної криптографічної хеш-функції, такої як MD5 або SHA1 / 2/256/512. Справа в тому, що відбувається одностороннє відображення. Його завжди багато: 1 відображення (тобто завжди буде зіткнення), оскільки кожна функція дає менший вихід, ніж здатна вводити (Якщо ви подасте всі можливі файли 1 Мб в MD5, ви отримаєте тонну зіткнень).
Причина, по якій їх важко (або неможливо практично), полягає в тому, як вони працюють внутрішньо. Більшість криптографічних хеш-функцій багато разів повторюють набір входів для отримання виводу. Отже, якщо ми подивимось на кожен фрагмент вхідної фіксованої довжини (який залежить від алгоритму), хеш-функція буде називати цей стан. Потім він повторить стан і змінить його на новий і використовуватиме це як зворотній зв'язок із собою (MD5 робить це 64 рази для кожного 512-бітного фрагмента даних). Тоді якимось чином поєднуються результуючі стани з усіх цих ітерацій разом, щоб утворити отриманий хеш.
Тепер, якщо ви хотіли розшифрувати хеш, спершу потрібно розібратися, як розділити даний хеш на його ітераційні стани (1 можливість для входів, менших за розмір фрагмента даних, багато для більшого введення). Тоді вам потрібно буде змінити ітерацію для кожного стану. Тепер, щоб пояснити , чому це дуже важко, уявіть собі , намагаючись вивести a
і b
з наступної формули: 10 = a + b
. Є 10 позитивних комбінацій, a
і b
це може спрацювати. Тепер прокрутіть цю групу разів:tmp = a + b; a = b; b = tmp
. Для 64 ітерацій у вас буде більше 10 ^ 64 можливостей спробувати. І це просто просте доповнення, де певний стан зберігається від ітерації до ітерації. Реальні хеш-функції виконують набагато більше, ніж 1 операція (MD5 робить близько 15 операцій на 4 змінних стану). А оскільки наступна ітерація залежить від стану попереднього, а попередня руйнується при створенні поточного стану, визначити вхідний стан, який призвів до заданого вихідного стану (для кожної ітерації не менше), майже неможливо. Поєднайте це, якщо велика кількість залучених можливостей і декодування навіть MD5 забирає майже нескінченну (але не нескінченну) кількість ресурсів. Стільки ресурсів, що це "
Функції шифрування
Вони забезпечують зіставлення 1: 1 між входом і виходом довільної довжини. І вони завжди оборотні. Важливо зазначити, що це оборотно, використовуючи якийсь метод. І це завжди 1: 1 для даного ключа. Тепер є кілька входів: пари клавіш, які можуть генерувати один і той же вихід (насправді вони зазвичай є, залежно від функції шифрування). Хороші зашифровані дані не відрізняються від випадкового шуму. Це відрізняється від хорошого хеш-виводу, який завжди має послідовний формат.
Використовуйте випадки
Використовуйте хеш-функцію, коли ви хочете порівняти значення, але не можете зберігати просте подання (з будь-якої кількості причин). Паролі повинні дуже добре відповідати цьому випадку використання, оскільки ви не хочете зберігати їх у простому тексті з міркувань безпеки (і не повинні). Але що робити, якщо ви хочете перевірити файлову систему на наявність піратських музичних файлів? Зберігати 3 мб на музичний файл було б недоцільно. Тому замість цього візьміть хеш файлу і збережіть його (md5 зберігатиме 16 байт замість 3mb). Таким чином, ви просто хеш-файли кожного файлу і порівняти зі збереженою базою хешей (Це не працює так добре на практиці через повторне кодування, зміну заголовків файлів тощо, але це приклад використання).
Використовуйте хеш-функцію під час перевірки дійсності вхідних даних. Ось для чого вони розраховані. Якщо у вас є 2 фрагменти введення, і ви хочете перевірити, чи вони однакові, запустіть обидва за допомогою хеш-функції. Ймовірність зіткнення є астрономічно низькою для невеликих розмірів вводу (якщо припустимо хорошу хеш-функцію). Ось чому це рекомендується для паролів. Для паролів до 32 символів md5 має 4-кратний простір виводу. SHA1 має 6-кратний вихідний простір (приблизно). SHA512 має приблизно в 16 разів більше місця на виході. Вас не цікавить, який був пароль , ви хвилюєтеся, чи він такий, як той, що зберігався. Ось чому слід використовувати хеші для паролів.
Використовуйте шифрування щоразу, коли вам потрібно буде вимкнути вхідні дані. Зауважте, слово потрібно . Якщо ви зберігаєте номери кредитних карт, вам потрібно буде їх повернути в якийсь момент, але не хочете зберігати їх звичайним текстом. Тому замість цього зберігайте зашифровану версію і зберігайте ключ максимально безпечним.
Функції хешу також чудово підходять для підписання даних. Наприклад, якщо ви використовуєте HMAC, ви підписуєте фрагмент даних, взявши хеш даних, об'єднаних з відомим, але не переданим значенням (секретним значенням). Отже, ви надсилаєте звичайний текст та хэш HMAC. Потім приймач просто хеширує подані дані з відомим значенням і перевіряє, чи відповідає вони переданому HMAC. Якщо це те саме, ви знаєте, що його не було підроблено стороною без секретного значення. Це зазвичай використовується в захищених системах файлів cookie в рамках HTTP-систем, а також при передачі повідомлень даних через HTTP, де ви хочете певну впевненість у цілісності даних.
Примітка про хеші паролів:
Ключова особливість криптографічних хеш-функцій полягає в тому, що їх потрібно створювати дуже швидко і дуже важко / повільно повертати (настільки, що це практично неможливо). Це створює проблему з паролями. Якщо ви зберігаєте sha512(password)
, ви не займаєтесь захистом від веселкових столів або грубих атак. Пам'ятайте, хеш-функція була розроблена для швидкості. Тож для зловмисника банально просто запустити словник через хеш-функцію і перевірити кожен результат.
Додавання солі допомагає вирішити питання, оскільки вона додає трохи невідомих даних у хеш. Тож замість того, щоб знайти що-небудь, що відповідає md5(foo)
, їм потрібно знайти те, що при додаванні до відомої солі виробляє md5(foo.salt)
(що зробити це набагато важче). Але це все ще не вирішує проблему зі швидкістю, оскільки якщо вони знають сіль, це лише питання проходження словника.
Отже, є способи боротьби з цим. Один популярний метод називається зміцненням ключів (або розтягуванням ключів). В основному ви повторюєте хеш багато разів (зазвичай це тисячі). Це робить дві речі. По-перше, це значно уповільнює час виконання алгоритму хешування. По-друге, якщо реалізовано правильно (передача вводу та солі на кожній ітерації) фактично збільшує ентропію (наявний простір) для виходу, зменшуючи шанси на зіткнення. Тривіальна реалізація:
var hash = password + salt;
for (var i = 0; i < 5000; i++) {
hash = sha512(hash + password + salt);
}
Є інші, більш стандартні реалізації , такі як PBKDF2 , Bcrypt . Але ця методика використовується досить багатьма системами, що стосуються безпеки (наприклад, PGP, WPA, Apache та OpenSSL).
Суть, hash(password)
недостатньо хороша. hash(password + salt)
краще, але все ще недостатньо добре ... Використовуйте розтягнутий хеш-механізм для створення хешей для паролів ...
Ще одна примітка про тривіальне розтягування
Ні в якому разі не подайте вихід одного хеша безпосередньо в хеш-функцію :
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash); // <-- Do NOT do this!
}
Причина цього пов'язана із зіткненнями. Пам'ятайте, що всі хеш-функції мають зіткнення, оскільки можливий вихідний простір (кількість можливих виходів) менший, ніж тоді вхідний простір. Щоб зрозуміти, чому, давайте подивимось, що відбувається. Для передмови це зробимо припущення, що існує шанс зіткнення 0,001% від sha1()
( насправді це значно нижче, але для демонстраційних цілей).
hash1 = sha1(password + salt);
Зараз hash1
є ймовірність зіткнення 0,001%. Але коли ми робимо наступне hash2 = sha1(hash1);
, всі зіткнення hash1
автоматично стають зіткненнямиhash2
. Отже, у нас показник хеш1 становить 0,001%, і другий sha1()
виклик додає до цього. Тож зараз hash2
є ймовірність зіткнення 0,002%. Це вдвічі більше шансів! Кожна ітерація додасть 0.001%
до результату ще один шанс зіткнення. Так, з 1000 ітерацій шанс зіткнення підскочив з тривіальних 0,001% до 1%. Тепер деградація лінійна, і реальні ймовірності набагато менші, але ефект той самий (оцінка ймовірності одного зіткнення з md5
приблизно 1 / (2 128 ) або 1 / (3x10 38). Хоча це здається малим, завдяки нападі на день народження, це насправді не так мало, як здається).
Натомість, повторно додаючи сіль та пароль кожен раз, ви знову вводите дані назад у хеш-функцію. Тож будь-яке зіткнення будь-якого конкретного раунду більше не є зіткненням наступного раунду. Тому:
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash + password + salt);
}
Має такий самий шанс зіткнення, як і вроджена sha512
функція. Якого ти хочеш. Використовуйте це замість цього.