Зберігання паролів користувачів, Безпека
Випадки досвіду розробки різних WEB проектів. Цікаві факти, статті, враження. Програмування та все про нього у сфері WEB.
Зберігання паролів користувачів

Я звичайно не заперечуватиму що статей в інтернеті на цю тему достатньо, але жодна на мій погляд не висвітлює всіх тонкощів здавалося б такого простого процесу. Для розуміння цих тонкощів розробнику необхідна наявність вміння оцінювати систему з боку зломщика, а для цього потрібний хоча б невеликий досвід у цій сфері (за принципом «хто попереджений, той озброєний»). На жаль, більшість розробників такого досвіду не мають, і безпеку своїх додатків вони будують на основі мізерної інформації знайденої в інтернеті.
У цій статті спробую розглянути мінуси зберігання відкритих паролів у БД. Спробую переконати у необхідності хешувати кожен пароль. Також спробую пояснити навіщо потрібна сіль і якою вона буває. Та й коротко розповім про різні алгоритми хешування.
Для початку нам необхідно зрозуміти навіщо взагалі потрібно правильно зберігати паролі. Правильно організований алгоритм зберігання паролів повинен:
- Зменшити ризик повного злому системи
- Запобігти витоку паролів користувачів
Розглянемо коли система має якісь теоретичні вразливості. Неправильний алгоритм дозволить у разі отримати пароль адміністратора через якусь наявну вразливість, далі потрапити до адмін-панель, і далі по обстановці, тобто. у 90% випадків це означає повний злом. У другому випадку, якщо зловмисник якимось чином отримуєбазу всіх паролів, це дозволить скомпрометувати деяких користувачів, які, наприклад, використовують один пароль попри всі ресурси. Правильний алгоритм повинен взагалі запобігти отриманню зловмисником паролів.
Насправді це виглядає так. Зловмисник знаходить на сайті SQL-injection, через яку отримує логін-паролі всіх користувачів. При правильній системі зберігання паролів зловмисник отримує не вихідні паролі, а тільки їх хеш суми (див. далі).
На зорі розвитку інтернету та веб-додатків, кожен розробник не замислювався над цією проблемою і зберігав у базі даних відкриті паролі. Але вище я вже описав чим поганий такий варіант. У результаті система зламувалась через найбанальнішу ін'єкцію до БД, яких у ті часи було дуже багато.
Розробники дійшли висновку, що використовувати відкрите зберігання паролів у своїх системах заняття не безпечне і треба придумати щось інше. І тут на допомогу прийшли суми хеш.
Що таке хеш сума? Допустимо у користувача пароль «123456». Вигадаємо свою хеш-функцію. Складемо всі цифри отримаємо число «21» — це буде результатом нашої хеш функції, хеш-сумою або хешом. Звичайно наш алгоритм огидний (та й хеш сумою його назвати не можна, це скоріше контрольна сума), оскільки один і той же хеш може відповідати великій кількості паролів. Тобто паролі «654321», «555222», «100299» тощо даватимуть такий самий хеш, або по-науковому будуть колізією.
Ідеальна хеш функція повинна мати наступні параметри:
- Необоротність — хеш сума не повинна «розшифровуватися» подібно до звичайних алгоритмів шифрування
- Відсутність колізій — для кожних даних, що проходять через хеш-функцію, повинен вийти унікальний хеш
Іякщо перший параметр практично досягнуто у сучасних алгоритмах хеш-функцій. То другий параметр не можна досягти для хешей з фіксованою довжиною (а таких алгоритмів зараз більшість) навіть чисто теоретично (я сподіваюся ви розумієте чому).
Тепер зловмиснику необхідно буде спробувати відновити пароль з хешу, і якщо алгоритм хеш-функції повністю незворотний, то для зловмисника залишиться лише один метод — брутофорс. Якщо більш зрозумілою мовою, то брутофорс — це перебір усіх можливих паролів, поки хеш від одного з них не збігається з вихідним хешем.
«Солені» хеші
Взагалі все б добре, але якби не ліниві або забудькі користувачі які прагнуть використовувати максимально короткі і прості паролі ... Чому це погано? Тому що збрутити короткі паролі можна за лічені хвилини, а прості (часто використовувані) паролі лаються за словниками.
Здавалося б вихід - заборонити користувачам використовувати короткі паролі, зобов'язати їх використовувати спецсимволи тощо. Але це справа кожного користувача який йому використовувати пароль. Ми, як розробники, можемо лише рекомендувати використовувати більш складний пароль.
Як же нам захистити користувачів та свій ресурс у разі його злому? На допомогу проходить сіль. Грубо кажучи сіль — це набір випадкових символів щоразу перед проходженням через хеш-функцію додається до пароля. При реєстрації користувача генерується випадкова сіль, на основі якої і вказаного пароля генерується «солоний» хеш, при цьому сіль також заноситься в БД: Що дає сіль у цьому випадку? Якщо подумати, то якщо зловмисник має доступ до хеш користувачів, то якщо сіль кожного хеша ми зберігаємо поруч (у тій же таблиці/БД), то зловмисник також матиме і доступдо солі. Тобто зможе знайти вихідний пароль методом брутофорсу, але словники йому вже не підійдуть, тому що не існує словників паролів, що враховують усі комбінації, з сіллю.
Взагалі такий алгоритм, якщо ви пам'ятаєте початок статті, запобігає витоку паролів користувачів.
Двічі «солоний» хеш
Але інша сторона медалі. У разі цілеспрямованої атаки на певного користувача (наприклад адміністратора ресурсу), зломщика не зупинить таку сіль, як написано вище, тому що йому доведеться лаяти лише один хеш з вже прийнятною швидкістю.
У нашому випадку допоможе ще одна сіль, але вже загальна для всіх хешей, яка допустимо буде зберігатися окремо від хешу, тобто в будь-якому іншому місці, наприклад, у конфізі самого додатка.
Давайте трохи поміркуємо. Відставте убік у свої випади у стилі «треба припускати, що зломщик має повний доступ до всієї системи і така сіль безглузда». Уявіть ситуацію, яку я описував вище, у вашій системі є вразливість на кшталт SQL-injection, через яку зловмиснику вдалося отримати адміністраторський хеш та сіль з БД. Далі зловмисникові вдалося і збрудити хеш — і все, ваша система, вважайте, зламана.
Якщо підбити підсумок, то сенс цієї солі полягає у зниженні ризику повного злому системи за наявності у зловмисника лише часткового доступу.
Алгоритми хешування
Звичайно, повністю захиститися від брута неможливо. Але в наших силах зробити брутофорос безглуздим. У своїх системах необхідно застосовувати такий алгоритм хешування, який потребує досить великих ресурсів та великої кількості операцій для обчислення хешу.
Тепер уявіть, що ми використовуємо алгоритм, який дозволяє генерувати хеші зі швидкістю лише тисяча або сотня хешей всекунду. Такий хеш разом з паролем хоча б у 10-12 знаків підбиратиметься значно довше, ніж «розумний час», і сенсу такий підбір не матиме.
Наприклад, на моєму ноуті:
- MD5 - 2 200 000 паролів/сек
- SHA - 800 000 паролів/сек
- MD5(unix) - 1 200 паролів/сек
Тепер давайте додамо трохи математики, і підрахуємо середній час, який знадобиться нам для перебору простенького пароля з 6 симолів латиниці верхнього та нижнього регістрів та цифр. Тобто це лише близько 100 000 000 000 комбінацій.
-
ДляMD5
45 000 секунд або
12 годин ДляSHA
125 000 секунд або
35 годин ДляMD5(unix)
83 300 000 секунд або
23 100 годин або
Причому для md5 і sha брут ще якось більш-менш має сенс, тобто md5(unix), на мій погляд, сенсу немає абсолютно. До речі, для довідки в основі md5(unix) лежить тисяча ітерацій звичайного md5.
Звичайно можна вигадати свій алгоритм, який буде обчислювати хеш ще довше, але тут необхідно знайти межу між ресурсомісткістю алгоритму та продуктивністю сервера. Інакше ви ризикуєте підвісити сервер лише одними обчисленнями хеша.
На жаль, все було б занадто добре якби все так було б просто. Але ми забули про колізії, про які я писав на самому початку. Не варто забувати про те, що для вашого хеша від супер складного пароля в 30 символів може бути знайдена колізія довжиною в 1 символ. Звичайно, ймовірність ця вкрай мала, але вона є.
І на превеликий жаль на даний момент не існує стовідсоткового вирішення цієї проблеми для хеш сум, тому що теоретично будь-хеш фіксованої довжини матиме колізії. Але зазвичай для зниженняймовірності знаходження колізей використовують кілька алгоритмів хешування. На практиці це може виглядати так: один пароль хешують спочатку md5, потім той же пароль хешируют по sha, отримані хеш-суми об'єднують в один хеш, який і використовують надалі.
Але в мене ніби виникла ще одна ідея для вирішення цієї проблеми, але про це в одній з наступних статей ;)
Ось основні правила, які ви повинні були зрозуміти з цієї статті:
- Пароль не повинен зберігатися у БД у відкритому вигляді, а повинна зберігатись лише хеш-сума
- При реєстрації користувача бажано рекомендувати (але не змушувати!) використовувати складніший пароль
- Кожен хеш користувача необхідно генерувати з унікальною сіллю
- До користувальницької солі повинна бути додана загальна сіль, яка зберігається в іншому місці (окремо від даних користувача)
- Сіль має бути досить довгою
- Алгоритм обчислення хеш суми має бути ресурсомісткий (але не вішати ресурс геть)