Атаки на пароли и их взлом
Офлайн-взлом — это задача экономики: быстрый несолёный хеш превращает украденный дамп в бесплатный словарь, а медленный солёный делает тот же дамп бесполезным. Разберём математику догадок в секунду, объясняющую, почему всё решают work factor и соль на пользователя.
На авторизованном задании тестировщик выгружает таблицу users из лабораторной базы и смотрит на колонку хешей паролей. Пока ничего не расшифровано — хеш односторонний, красть нечего, ключа нет. И всё же в течение часа рядом с третью аккаунтов появляются открытые пароли. Не потому, что тестировщик сломал математику, а потому, что он арендовал GPU-стенд, а приложение хранило пароли как чистый SHA-256. На таком железе одна карта перебирает миллиарды кандидатов SHA-256 в секунду; весь словарь RockYou из четырнадцати миллионов утёкших паролей заканчивается за доли секунды, а полный перебор всех 8-символьных строк из строчных букв завершается до обеда. Парольная политика защитника перестала что-либо значить после утечки дампа. Значение имело одно проектное решение, принятое много лет назад: какая функция превратила пароль в этот хеш и была ли соль. Этот урок — вскрытие того решения.
К концу урока вы сможете рассуждать об офлайн-взломе как о задаче экономики — догадки в секунду умножить на стоимость хеша — и объяснять, почему медленный солёный хеш и есть тот контроль, который решает, окажется украденный дамп катастрофой или бесполезным мусором.
Сначала авторизация: это вскрытие глазами защитника
Всё ниже предполагает подписанное задание с явно определённым скоупом — заведомо уязвимую лабораторию, CTF, дамп хешей из вашей собственной системы или цель внутри программы bug bounty. Запуск взломщика против учётных данных, которые вы не имели права получать, — это несанкционированный доступ в большинстве юрисдикций, а «я просто хотел посмотреть, насколько они слабые» — не оправдание. Мы изучаем экономику атакующего по одной причине: нельзя оценить стоимость контроля, которого не понимаешь. Сам вопрос «достаточно ли bcrypt?» неразрешим, пока вы не умеете прикинуть, сколько догадок в секунду стоит ваш хеш атакующему и сколько их ему нужно. Читайте это как вскрытие, которое защитник проводит над собственным хранилищем учёток, — учитесь видеть колонку хешей так, как её видит взломщик, чтобы work factor был верным до того, как дамп когда-либо утечёт. Никаких готовых рецептов взлома здесь — только механизм и его числа.
Онлайн против офлайна: две совершенно разные игры
Есть две арены, и по сложности они не близки. Онлайн-атака подбирает пароли против живой системы — отправляя их в форму входа, в API, на SSH-демон. Здесь часами управляет защитник: rate limiting, блокировка аккаунта, экспоненциальная задержка и CAPTCHA ограничивают атакующего парой догадок в секунду на аккаунт, и каждая догадка видна в логах. Онлайн-подбор по замыслу медленный и шумный — поэтому доминирующая онлайн-атака сегодня вовсе не слепой перебор, а credential stuffing: повтор пар логин-пароль, уже утёкших из других пробоев, в расчёте на переиспользование. Эта ставка играет, потому что люди переиспользуют пароли; защита — это MFA и проверка по базам утёкших паролей, а не более быстрый хеш.
Офлайн-атака — совсем другая игра. Как только атакующий держит хеши — из дампа SQL-инъекции, украденного бэкапа, скомпрометированной реплики, — между ним и ответом больше нет формы входа. Он вычисляет хеши кандидатов на собственном железе, настолько быстро, насколько позволяет кремний, без rate limit, без блокировок и без единой записи в ваших логах. Защитник потерял контроль над временем; единственный оставшийся рычаг — насколько дорого обходится вычисление одного хеша. В этом весь стержень урока: онлайн-безопасность — про скорость; офлайн-безопасность — про стоимость одного хеша. Пароль из 12 символов за быстрым хешем тривиально взламывается офлайн и абсолютно безопасен онлайн — тот же пароль, противоположный исход, потому что у этих игр разная физика.
Экономика: догадки в секунду умножить на стоимость хеша
Офлайн-взлом сводится к одному неравенству, которое атакующий держит в голове: смогу ли я позволить себе достаточно догадок, чтобы покрыть вероятное пространство ключей раньше, чем данные перестанут стоить усилий? Два члена — это скорость (сколько хешей кандидатов в секунду считает железо) и пространство ключей (сколько кандидатов придётся перебрать). Защитник не может сжать пространство ключей — это пароль пользователя, — поэтому вся защитная игра сводится к обрушению скорости, а скорость задаётся почти целиком тем, какую хеш-функцию вы выбрали.
Числа жестокие, и их стоит усвоить. Современный GPU считает порядка десятков миллиардов догадок быстрого хеша в секунду для одного MD5 или SHA-256, а арендованный многокарточный стенд умножает это. Против такой скорости быстрый хеш — вообще не защита: весь список RockYou исчерпывается заметно меньше чем за секунду, а исчерпывающий перебор прогрызает короткие пространства ключей за минуты. Защита — выбрать намеренно медленный парольный хеш: bcrypt, scrypt, Argon2, специально сделанный дорогим. Против bcrypt с реалистичным cost factor тот же GPU падает с десятков миллиардов до нескольких десятков тысяч догадок в секунду — замедление примерно в миллион раз. Словарь, который раньше исчерпывался за долю секунды, теперь занимает часы; исчерпывающий перебор, заканчивавшийся до обеда, теперь переживает ценность данных. В пароле ничего не изменилось — изменилась только стоимость одного хеша, и один этот фактор сдвинул атаку с тривиальной на нерентабельную.
| Хеш-функция | Создана для | Догадок/сек на GPU (порядок) | Офлайн-вердикт |
|---|---|---|---|
| MD5 / SHA-1 / SHA-256 | Скорость (целостность файлов, не пароли) | ~1010–1011 | Дамп ≈ бесплатный словарь; не использовать для паролей |
| bcrypt (cost 12) | Медленность, настраиваемый work factor | ~104 | ~106× медленнее; словарь теперь занимает часы |
| scrypt / Argon2id | Медленность + memory-hardness | ~103–104 | Стоимостью RAM бьёт и по дешёвому параллелизму GPU/ASIC |
Соль: почему случайное значение на пользователя убивает предвычисление
Work factor замедляет вычисление одного хеша; соль ломает способность атакующего амортизировать работу по множеству хешей. Соль — это уникальное случайное значение, хранимое рядом с каждым хешем и подмешиваемое во вход перед хешированием, так что два пользователя с одинаковым паролем Summer2024! получают два совершенно разных хеша. Одно это свойство разрушает сразу три экономики атакующего. Во-первых, оно убивает rainbow-таблицы — гигантские предвычисленные таблицы соответствия, отображающие хеши обратно в открытый текст: rainbow-таблица строится для несолёных хешей, а соль на пользователя означала бы, что атакующему нужна отдельная таблица под каждую соль, что вычислительно абсурдно, — и предвычисление схлопывается обратно в живой перебор. Во-вторых, оно убивает дедупликацию хешей: в несолёном дампе все аккаунты с самым популярным в мире паролем делят один хеш, так что взломав его однажды, взламываешь их все; соль заставляет атаковать каждый хеш по отдельности. В-третьих, она убирает мгновенный сигнал о том, что два пользователя делят пароль.
Ключевой нюанс для зрелого инженера: соль не секретна и не должна быть секретной. Она хранится в открытом виде прямо рядом с хешем, и это нормально — её задача в уникальности, а не в конфиденциальности. Соль никак не замедляет взлом одного конкретного хеша; если атакующему нужен пароль одного определённого пользователя, соль защитнику вообще не помогает. Вся ценность соли — на масштабе: она превращает «взломай дамп однажды — разблокируй всех» в «взламывай каждый аккаунт с нуля, по одному», что в сочетании с медленным work factor и делает дамп всей базы экономически безнадёжным. Современные парольные хеши (bcrypt, Argon2) генерируют и встраивают соль за вас — именно поэтому нельзя городить это вручную на голом быстром хеше.
▸Почему это работает
Почему GPU-железо так разрушительно именно против быстрых хешей и почему Argon2 добавляет стойкость по памяти поверх медленности? Быстрый хеш вроде SHA-256 — это крошечная безсостоянийная арифметика, ровно та нагрузка, что тысячи мелких ядер GPU гонят в массовом параллелизме, а ASIC вшивает в кремний ещё дешевле. Чистая медленность (cost factor у bcrypt) помогает, но более богатый атакующий всё равно может бросить на задачу больше ядер. Стойкость по памяти снова меняет экономику: Argon2id и scrypt заставляют каждый хеш выделять крупный блок RAM, а RAM дорого тиражировать по тысячам параллельных ядер или класть на ASIC. Так стоимость памяти ограничивает, насколько атакующий может распараллелиться, и притупляет преимущество GPU/ASIC, которое чистая вычислительная медленность оставляет открытым. Поэтому текущие рекомендации тянутся в первую очередь к Argon2id, с bcrypt как хорошо изученной запасной опцией.
Как защитник оценивает стоимость контроля
Сложите детали — и задача защитника в том, чтобы офлайн-взлом стоил дороже, чем стоят данные. Это три решения по порядку. Выбрать медленный, стойкий по памяти парольный хеш (Argon2id или bcrypt/scrypt) — никогда голый быстрый хеш, никогда шифрование (шифрование обратимо; вам нужна односторонность). Настроить work factor до наибольшей стоимости, какую терпит ваш бюджет задержки входа — ориентировочно стремясь к доле секунды на хеш на собственном железе, — и пересматривать его по мере ускорения железа, потому что cost factor, выбранный в 2015-м, сегодня слишком дёшев. Полагаться на автоматическую соль на пользователя из библиотеки, чтобы один взлом никогда не обобщался на всю базу. И принять, что хеширование — последний рубеж, а не единственный: ограничьте онлайн-игру через rate limiting, блокировки и MFA, а новые пароли проверяйте по базам известных утечек, чтобы credential stuffing нечего было повторять. Зрелый рефлекс тот же, что и везде в безопасности: считайте, что дамп рано или поздно утечёт, и убедитесь, что контроль уже оценён так, что, когда это случится, арифметика атакующего не сходится.
Вы упрочняете хранилище учётных данных перед запуском. Текущий дизайн хранит пароли как чистый SHA-256. На авторизованном ревью вы подтвердили, что один GPU взломал бы большую часть утёкшего дампа меньше чем за час. Выберите изменение, которое действительно закрывает офлайн-экспозицию.
Один и тот же 12-символьный пароль абсолютно безопасен против онлайн-атаки на форму входа, но тривиально взламывается, как только утекает дамп хешей (при условии быстрого хеша). Почему исходы противоположны?
Ревьюер говорит: «мы добавили соль на пользователя, так что наше хранилище паролей на SHA-256 теперь безопасно против офлайн-взлома». Что не так с этим утверждением?
Упорядочьте шаги кампании офлайн-взлома против дампа с быстрым несолёным хешем, от получения до результата:
- 1 Атакующий получает дамп хешей (SQLi, украденный бэкап, скомпрометированная реплика)
- 2 Определяет хеш-функцию и подтверждает, что она быстрая и несолёная
- 3 Прогоняет словарь (например, RockYou) частых/утёкших паролей на GPU-железе
- 4 Откатывается на масочный перебор по оставшемуся короткому пространству ключей
- 5 Восстанавливает открытый текст для большой доли аккаунтов и пробует их в других местах (stuffing)
- 01Объясните, почему онлайн- и офлайн-атаки на пароли — совершенно разные игры, и какой единственный рычаг есть у защитника против офлайн-взлома.
- 02Пройдите по экономике офлайн-взлома и объясните, как work factor и соль по отдельности меняют арифметику атакующего — и почему одно без другого недостаточно.
Взлом паролей — это задача экономики, и зрелое прозрение в том, что единственный офлайн-рычаг защитника — стоимость одного хеша. Онлайн и офлайн — разные игры с разной физикой: онлайн защитник управляет временем через rate limiting, блокировки и MFA, поэтому живая угроза — это credential stuffing (переиспользованные пары из других пробоев), а не слепой подбор; офлайн атакующий держит дамп и считает догадки на своём железе без rate limit, без блокировок и без логов. Против быстрого несолёного хеша (MD5/SHA-256) один GPU идёт на десятках миллиардов догадок в секунду, так что дамп фактически бесплатный словарь — весь список RockYou исчерпывается меньше чем за секунду. Фикс — два ортогональных рычага: намеренно медленный, стойкий по памяти хеш (Argon2id, bcrypt, scrypt), режущий скорость атакующего в ~10^6 раз, плюс автоматическая соль на пользователя, убивающая rainbow-таблицы и межаккаунтную дедупликацию, чтобы один взлом никогда не обобщался. По отдельности ни одного не хватит — соль без медленности оставляет каждый хеш перебираемым, медленность без соли снова включает предвычисление, — а политика длины или утекаемый pepper им не замена. Теперь, читая хранилище учёток, вы задаёте тот же первый вопрос: сколько догадок в секунду стоит этот хеш атакующему, который уже держит дамп?
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.