Належне хешування паролів
Зломщики хешів у наші дні стали дуже ефективними, настільки, що більшість паролів можна зламати за незначний час. Навіть довгі паролі, піддані одноразовому хешуванні, перестали бути безпечними.
1. Введення
Очевидно, що ми мали написати цю статтю, оскільки незліченні сервіси, яким ми довіряємо наші паролі, поводяться з ними безвідповідально. Зломщики хешів у наші дні стали дуже ефективними, настільки, що більшість паролів можна зламати за незначний час. Навіть довгі паролі, піддані одноразовому хешуванні, перестали бути безпечними. Спочатку ми розглянемо поточний стан речей, далі те, чому ваш сервіс не повинен легко ставитися до цієї проблеми, а потім різні способи, якими ви можете поліпшити ваше сховище паролів.
1.1 Поточне становище
Як свідчать нещодавні дампи різних баз даних паролів, більшість веб-сайтів не зберігають належним чином паролі своїх користувачів. Зберігання паролів відкритим текстом або єдиним алгоритмом хешування без солі здається цілком традиційним. Це безвідповідально. Якщо ви дійсно хочете зберегти собі мілісекунди обчислювального часу замість нормального захисту своїх користувачів, тоді я особисто і відкрито висловлюватимусь проти вашого сервісу.
1.2 Моральне зобов'язання
Якщо ви будь-яким чином зберігаєте чиїсь паролі, ви несете за них відповідальність. Щоразу, коли хтось зламує базу даних і розкриває паролі, що зберігаються у відкритому вигляді, або слабкі хеші, всі постраждалі користувачі повинні не тільки змінити свої паролі до кожного сервісу, для якого використовувався розкритий пароль, але й задуматися, чи хочуть вони продовжувати використовувати зламаний сервіс. Якщо ви не захищаєтепаролі належним чином, у якийсь момент часу ви відчуєте наслідки цього. Витратьте час на належну генерацію та зберігання хешів паролів та ентропії.
2 Належні методи хешування
Просто хешувати пароль недостатньо. Потрібно робити це належним чином. Ось кілька методів та керівних вказівок.
2.1 Сіль хороша для вас
Сіль, що стосується хешей, метафорично схожа на харчову сіллю. Ви також приправляєте їй хеш. Єдина відмінність у тому, що ми хочемо щось більше при використанні солі в хеш. Солі паролів – тип ентропії, що робить різними хеші, обчислені за однаковим алгоритмом від тексту. Це робиться шляхом хешування солі разом із відкритим текстом. Однієї солі недостатньо для забезпечення безпеки хеш, але з нею хеш краще, ніж без неї.
Сіль також використовується для збільшення тимчасової складності атак за словником та райдужними таблицями. Тепер атакуючому потрібно як згенерувати таблиці з потенційними паролями, йому потрібно згенерувати їх із різним значенням солі. Ось чому важливо, щоб для кожного хеша використовувалася унікальна сіль (переважно є випадковим набором байтів): не тільки, щоб гарантувати відсутність колізії паролів користувача, якщо вони однакові, але і щоб збільшити обчислювальну складність і розмір атаки за словником або перебором. Не повторюйте сіль.
2.2 Функції отримання ключів
Досить цікаво, що найкращі функції для хешування паролів спочатку призначалися для генерації ключів шифрування зі значень, які не підходять на роль ключа. Дані функції обробляють вхідні дані криптографічно і повертають набір байтів, які можуть бути використані, як ключ шифрування. Вони також є односпрямованимифункціями шифрування, які виробляють більше обробки даних, ніж алгоритми хешування, легко можуть бути налаштовані для більшої стійкості.
2.2.1 PBKDF2
PBKDF2 (Password-Based Key Derivation Function 2 або Функція Отримання Ключу На Основі Пароля 2) – це функція отримання ключа, розроблена RSA Laboratories, яка використовується для отримання стійких ключів на основі хешу. Вона працює шляхом застосування псевдовипадкової хеш-функції (на кшталт SHA-256, SHA-512, і т. д.) до рядка, у нашому випадку – до паролю, разом із сіллю та повторенням цього процесу велику кількість разів. Цей процес може бути узагальнений наступною діаграмою:

1bytes PBKDF2(HashAlgo, Plaintext, Salt, Iterations) < bytes IterativeHash = hash_hmac(HashAlgo, Salt, Plaintext); bytes TempHash = IterativeHash; for(int iter = 0; iter
Це спрощена версія повного псевдокоду. Вона також модифікована, щоб ми змогли оптимально використовувати її як функцію хешування, а не як функцію отримання ключа. Ось її PHP-реалізація:
Хоча наведені вище приклади і узагальнені, вони дають базовий погляд на те, як може бути реалізована функція PBKDF2. Тут лише хешується сіль і відкритий текст для отримання першого хеш, потім у циклі той же алгоритм використовується для обчислення хеш від відкритого тексту і результату попередньої ітерації, після чого повертається результат застосування операції XOR до всіх обчислених хеш. Виконавши цю операцію 1000 або більше разів, ви згенеруєте сильний, стійкий до злому хеш, який можна безпечно використовувати для зберігання паролів. За подробицями зверніться до NIST-SP800-132.
2.2.2 ARC4PBKDF2
Я (bwall) деякий час обігравав у думці ідею динамічної ентропії. PBKDF2 здається гарноюмайданчиком для її впровадження. Ця ідея полягає в тому, щоб зробити ентропію алгоритму шифрування, що змінюється в ході шифрування. Вона походить з обмежень деяких систем швидкого злому хешей, які мають тенденцію жорстко оптимізувати певні процеси на кшталт застосування ентропії. Щоб боротися з цим, ми можемо додати відносно швидкий процес створення додаткової ентропії, який вимагатиме додаткових обчислень під час злому хешу та можливого видалення деяких оптимізацій. В ARC4PBKDF2 потік шифру ARC4 ініціалізується ключем, потім даний шифр використовується для шифрування відкритого тексту перед його використанням у HMAC у кожній ітерації, продовжуючи той самий потік і шифруючи результат HMAC. Цей потік ARC4 повинен бути єдиним для всього процесу, щоб додати складності і без того важкий для злому метод хешування. Далі наведено приклад реалізації на C#.
public byte[] Hash(byte[] input) < ARC4 rc4 = новий ARC4(ARC4Key); byte[] derived = new HMACSHA256(Salt).ComputeHash(rc4.CryptBytes(input)); byte[] temp = derived; for (int x = 0; x
Важливо, що у реалізації ARC4 метод CryptBytes продовжує використовувати один потік ARC4, отже кожне шифрування виробляється у різних частинах потоку. Динамічна ентропія – нова, експериментальна ідея, яку ще доведеться обговорити тим, хто розробляє методи оптимізації, на боротьбу з якими вона спрямована.
2.2.3 bcrypt
По суті, bcrypt - це блоковий шифр Blowfish4, що використовується в режимі ECB, з більш складним алгоритмом підготовки ключів (особливо щодо S-блоків). Це дозволяє алгоритму бути стійким до можливих майбутніх атак і значно адаптивнішим. Реалізація алгоритму використовує 128-бітову сіль та вдосконаленийалгоритм, відомий, як eksblowfish або expensive key schedule blowfish. Заголовок функції bcrypt виглядає так:
bcrypt(cost, salt, pwd)
Тут cost – контролер підготовки ключів (задає ресурсомісткість фази підготовки ключів), salt – 128-бітове значення, а pwd – текстовий (до 56 байтів) ключ, використовуваний для шифрування алгоритму Blowfish.
Існує пара дійсно дивовижних, корисних речей, що стосуються bcrypt і заслуговують на обговорення. Одна з них – те, що алгоритму НЕОБХІДНА сіль. Хоча сіль сама по собі не збереже ваш вкрадений SQL-дамп, вона збільшить його стійкість. Друга – те, що алгоритм є (я використав цей термін уже кілька разів) адаптивним. Час підготовки ключів може бути всього близько однієї мілісекунди або такий великий, як ви захочете. Чудовий факт полягає в тому, що для сервера майже байдуже, що перевірка пароля займає 3 секунди замість 0,3, але для атакуючого таке збільшення складності - абсолютний хаос. Це також дозволяє безпосередньо боротися з експоненційним нарощуванням обчислювальних потужностей (відомим як закон Мура), оскільки з часом ви можете поступово змінювати значення змінної cost, щоб збільшити складність паролів.
public string bcrypt(int cost, byte[] salt, byte[] password) < byte[] state = EksBlowFishSetup(cost, salt, password); string ciphertext = "OrpheanBeholderScryDoubt"; for (int i = 0; i
Наведений вище код досить простий. Спочатку значення змінної state встановлюється алгоритмом EksBlowFish - ця стадія є найбільш ємною за часом. Далі йде цикл отримання шифртексту, що спочатку має значення 192-бітного магічного рядка (майже завжди рівної "OrpheanBeholderScryDoubt"), шляхом багаторазового шифрування шифртексту зпопередньої ітерації у режимі ECB разом із значенням змінної state. Підсумковий результат – статична 192-бітова конкатенація вартості, солі та результуючого шифртексту.
3 Висновок
Розглянуті методи значно збільшують час, необхідний злому хешів. Не існує приводу не використовувати ці методи для зберігання паролів. Ми розуміємо, що оперування хешами може бути складним, особливо якщо ви не розумієте їх як слід. Ми в Ballast Security працюємо над вирішенням цієї проблеми, щоб навіть ті, хто не має поняття, як убезпечити паролі, змогли б зробити це ціною мінімальних зусиль.