«Мы храним ваши пароли в зашифрованном виде» — фраза, после которой стоит насторожиться
Формулировка встречается в политиках конфиденциальности постоянно и звучит успокаивающе. На деле она означает одно из двух: либо в компании действительно шифруют пароли — и это плохо, — либо там не различают шифрование и хеширование, что тоже не добавляет уверенности.
Чем хеширование отличается от шифрования (простите за базу, но кто-то её не знает)
Шифрование обратимо. Есть ключ — есть исходный текст. Значит, где-то на сервере лежит ключ, которым расшифровываются пароли всех пользователей сразу. Утечка базы вместе с ключом — а лежат они обычно недалеко — отдаёт злоумышленнику пароли в открытом виде.
Хеширование необратимо. Из пароля считается значение, из которого пароль обратно не восстанавливается. При проверке хешируется введённое и сравнивается с сохранённым. Расшифровывать нечего, ключа нет.
Правильный ответ на вопрос «как вы храните пароли» звучит скучно: «мы их не храним, мы храним хеши». И вот здесь начинается вторая часть, которая интереснее.
Почему одного хеша недостаточно
Хеш-функции общего назначения делались быстрыми — это их достоинство для проверки целостности файлов и недостаток для паролей. Замеряю на обычном рабочем компьютере:
import hashlib, time
pw = b'CorrectHorse42'
t = time.perf_counter()
for _ in range(200_000):
hashlib.md5(pw).hexdigest()
print(f'{200_000 / (time.perf_counter() - t):,.0f} хешей в секунду')
md5 3 582 622 хешей в секунду
sha256 3 714 681 хешей в секунду
Три с половиной миллиона в секунду — без всякой оптимизации. На видеокарте это миллиарды. Утекшая база с md5-хешами перебирается по словарю за часы.
Функции, предназначенные для паролей, устроены наоборот — они намеренно медленные:
pbkdf2-sha256, 600 000 итераций — один хеш: 0.064 с
bcrypt cost=12 — один хеш: 0.183 с
Разница в семь порядков. Проверка пароля при входе занимает доли секунды и пользователю незаметна, а перебор из осмысленного превращается в бессмысленный: то, что раньше считалось часы, теперь считается годы.
Как понять, что сервис хранит пароли правильно
Если вы выбираете сервис, признак зрелости — формулировка «пароли хешируются с помощью bcrypt / scrypt / Argon2 с солью». Признак проблемы — «шифруются», «хранятся в защищённом виде», «доступ к базе ограничен».
Простая проверка, доступная всем: воспользуйтесь восстановлением пароля. Если сервис присылает ваш прежний пароль в письме — он хранит его так, что может прочитать. Это исчерпывающий ответ, никаких политик читать не нужно.
Если вы разработчик, актуальные рекомендации на сегодня выглядят так. Argon2id — предпочтительный вариант для новых проектов. bcrypt — проверенный, повсеместно доступный, подходит; параметр стоимости подбирается так, чтобы один хеш считался около сотни миллисекунд на вашем железе. PBKDF2 — приемлем там, где требуется сертифицированный алгоритм, с большим числом итераций.
Соль обязательна, генерируется случайно для каждого пароля. Современные библиотеки делают это сами и кладут соль в ту же строку, где лежит хеш. Перец (общий секрет, добавляемый к паролю и хранящийся отдельно от базы) — полезное дополнение, потому что утечка одной только базы становится бесполезной.
Отдельно про «сложность» и смену паролей
Раз уж речь о паролях, то отмечу ещё два хвоста, которые тянутся из прошлого десятилетия.
Требование «заглавная, цифра, спецсимвол» даёт предсказуемые пароли: люди выполняют его одинаково, дописывая в конец восклицательный знак и год. Длина работает лучше сложности.
Принудительная смена каждые три месяца снижает качество паролей: человек не придумывает новый, а меняет цифру в старом. Актуальные рекомендации NIST по цифровой идентификации от такого требования отказались. Вместо него — смена по событию и проверка нового пароля по спискам скомпрометированных.
Если вам это кажется очевидным — сходите и посмотрите, что написано в политике паролей вашей организации :)
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.