Що робить формат зберігання відновлюваним
Чотири формати зберігання, ранжовані за відновлюваністю:
| Формат | Відновлюваний? | Приклад | Метод відновлення |
|---|---|---|---|
| Відкритий текст | Так | password: hunter2 | Прочитати файл |
| Base64 | Так | cGFzc3dvcmQ= | base64 --decode |
| Зворотний шифр (AES) | Так | ENC[AES256:...] | Розшифрувати за допомогою ключа |
| Односторонній хеш (bcrypt) | Ні | $2b$12$... | Неможливо розшифрувати; потрібно brute-force |
Звичайний текст, base64 та зворотний шифр: усе відновлюване. Один дамп бази даних облікових даних дає зловмиснику всі паролі у відкритому вигляді для всіх користувачів одночасно. Один прорив — повне розкриття.
Приклад Mailman 2.x
Mailman 2.x (GNU-менеджер списків розсилки): зберігав паролі передплатників у відкритому тексті. Щомісячний лист-нагадування про пароль: надсилав усім передплатникам їхній пароль у відкритому вигляді. Два окремі дефекти, обидва MOAD-0006:
1. Зберігання: відкритий текст у базі даних списку. Компрометація сервера розкриває всі паролі передплатників.
2. Розсилка: щомісячний лист надсилає пароль у відкритому вигляді через SMTP на поштовий сервер передплатника. Лист подорожує у відкритому вигляді через кілька SMTP-вузлів.
Команда Mailman розробила обидві поведінки. Відновлення було функцією: підписники могли отримати забуті паролі. Назва Glass Safe походить саме від цього: сейф зберігає облікові дані у видимій формі. Будь-хто, хто отримує доступ до сейфа, може прочитати весь вміст одночасно.
Принцип «Вже вкрадено»
Облікові дані, збережені у відновлюваній формі, — це облікові дані, які вже вкрадено. Зловмисник ще не з’явився. Порушення ще не сталося. Але архітектура гарантує: коли відбудеться порушення, усі облікові дані потраплять до зловмисника одночасно. Жодне порушення не відбувається ізольовано; кожні облікові дані у відновлюваному сховищі передаються зловмиснику в одній операції.
MOAD-0006 vs MOAD-0004
MOAD-0004 (Logged Secret): облікові дані випадково записані в логи. Запис у лог не був наміром; це був побічний ефект увімкнення логування заголовків для налагодження.
MOAD-0006 (Glass Safe): облікові дані навмисно зберігаються у відновлюваній формі. Відновлення було наміром. Функція нагадування пароля вимагала зберігання пароля. Функція відображення пароля вимагала його зберігання. Архітектурне зобов’язання щодо відновлення створило дефект.
Однорядкове розрізнення: MOAD-0004 поміщає облікові дані в логи випадково; MOAD-0006 зберігає облікові дані у відновлюваній формі навмисно. Виправлення працюють на різних рівнях.
Структурне vs Випадкове
Архітектурна відмінність між MOAD-0006 та MOAD-0004 визначає стратегію виправлення. Випадковий запис у лог: виправити рівень серіалізації. Зберігання, спроектоване для відновлення: перепроектувати функцію, яка потребувала відновлення.
Чому bcrypt працює
Одностороння хеш-функція приймає пароль і видає дайджест фіксованої довжини. За наявності дайджесту вихідний пароль неможливо відновити. Не «важко відновити»: неможливо обернути. Функція працює лише в одному напрямку.
Три властивості, необхідні для зберігання облікових даних:
1. Односторонність (стійкість до відновлення прообразу). Маючи hash(password), жоден алгоритм не може відновити password швидше, ніж методом повного перебору. bcrypt, scrypt та argon2 задовольняють цю властивість.
2. Сіль. Випадкове значення, яке додається перед паролем перед хешуванням. Один і той самий пароль із різною сіллю дає різні хеші. Мета: протидія райдужним таблицям (словникам попередньо обчислених хешів). Без солі: зловмисник обчислює hash('password123') один раз і перевіряє всіх 1 мільйон користувачів одночасно. З сіллю: кожен користувач має унікальний хеш навіть для однакового пароля.
3. Спеціально сповільнені. bcrypt приймає коефіцієнт складності. Вищий коефіцієнт: більше ітерацій, більше обчислювального часу на спробу хешування. Логін: 300 мс на одне хешування. Прийнятно. Повний перебір: 300 мс на спробу. При 1 мільярді спроб: 9,5 років на пароль. Неприйнятно для зловмисника. Сповільнення — це функція, а не дефект.
import bcrypt
# Зберігання: односторонній хеш із сіллю
def store_password(plaintext: str) -> bytes:
return bcrypt.hashpw(plaintext.encode(), bcrypt.gensalt(rounds=12))
# Verify: hash the candidate & compare digests
def verify_password(plaintext: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(plaintext.encode(), stored_hash)
# NEVER stored: the plaintext password
# Ніколи не відновити: відкритий текст з хешу
# Скидання пароля, а не нагадування пароля
Компроміс
Одностороннє хешування робить відновлення пароля неможливим. Користувач, який забув свій пароль, не може отримати його назад. Лист із нагадуванням пароля не може існувати. Змінюється користувацький досвід: «забули пароль? скиньте його». Це не погіршення: це межа безпеки. Система, яка не може відновити пароль, не може його витекти.
Порушення бази даних, яке розкриває bcrypt-хеші: усі хеші видимі, паролі невидимі. Зловмисник повинен перебирати кожен хеш окремо, по 300 мс на спробу, а унікальна сіль для кожного користувача унеможливлює використання попередньо обчислених таблиць. Порушення, яке розкриває паролі у відкритому тексті: повне негайне розкриття.
Сильного шифрування недостатньо
Аудит безпеки виявляє систему зберігання облікових даних. Паролі зберігаються за допомогою AES-256-CBC з ключем на стороні сервера. Звіт аудиту позначає це як дефект «Glass Safe».
Команда інженерів відповідає: «AES-256 — найсильніший симетричний шифр, доступний на сьогодні. Ключ зберігається в апаратному модулі безпеки. Жоден зловмисник не зможе розшифрувати ці паролі.»