Пароль по словам: разбор идеи о многошаговой авторизации
Обычный вход устроен просто до примитивности. Есть поле для имени и поле для пароля, вы заполняете оба, нажимаете кнопку — и за один запрос отправляете на сервер всё сразу. Сервер сверяет и тут же отвечает: пустили или нет. Одна попытка — один ответ, мгновенно.
Именно эта мгновенность и не даёт покоя. Раз каждая попытка возвращает сразу понятный результат, её можно повторять машинально, тысячами в секунду. Так и работает перебор: берём словарь, скармливаем его форме входа, ждём, когда сервер скажет «да». Чем быстрее цикл «отправил — получил ответ», тем дешевле атака.
Отсюда напрашивается идея, которую я и хочу разобрать. А что если не отправлять пароль одним куском?
Суть предложения
Пусть пароль — это не строка, а несколько отдельных «слов». Каждое слово вводится и уходит на сервер само по себе, отдельным шагом. Вход превращается в цепочку: слово, потом ещё слово, потом ещё. А ответ — пустили или нет — приходит только в самом конце, на заранее условленном шаге. Скажем, после третьего слова. Или после восьмого.
От имени пользователя при этом, возможно, можно вообще отказаться и опознавать человека только по самой цепочке.
Логика за этим такая. Чтобы проверить одну догадку, переборщику теперь надо отправить не один запрос, а целую серию. Промежуточные шаги молчат — нельзя понять, на каком именно слове ты ошибся. А раз так, рассуждение продолжается, то и сами слова не обязаны быть особенно сложными.
Идея красивая. И, что интересно, она нащупывает сразу несколько вещей, которые в безопасности действительно важны. Но ровно в том месте, где она обещает главную выгоду, она и разваливается. Давайте по порядку — сначала про то, что угадано верно, потом про то, что нет.
Что в этой идее угадано верно
Не говорить, где именно ошибся. Это не выдумка, а давний принцип. Хорошо сделанный вход уже сейчас не сообщает, что неверно — имя или пароль. Он отвечает одинаково расплывчато в обоих случаях, потому что любая подсказка сужает переборщику пространство поиска. Больше того: грамотная проверка пароля сравнивает строки за постоянное время, не прерываясь на первом несовпавшем символе. Иначе появляется тонкая лазейка — атака по времени ответа: по тому, насколько быстро сервер сказал «нет», можно угадывать пароль по одному символу1. Так что «не выдавать, где ошибка» — это правильный инстинкт. Запомним его, он ещё пригодится.
Секретная последовательность как условие доступа. Это тоже не новость — это буквально существует и называется port knocking, «стук в порты». Сервис прячется за наглухо закрытым межсетевым экраном и открывает порт только тому, кто постучал в заранее условленную последовательность портов. Пока вся последовательность не пройдена правильно, снаружи всё выглядит закрытым, и — ключевой момент — сервер ничего не подтверждает на промежуточных шагах, он вообще не отвечает2. Есть и более зрелый вариант, single packet authorization, где весь «стук» упакован в один зашифрованный пакет с подписью и меткой времени3. То есть идея «пройди скрытую цепочку, и только тогда получишь доступ» в мире существует и работает.
Пароль из нескольких слов. И это давно живёт под именем «парольная фраза». Современные рекомендации, в том числе NIST, прямо советуют делать ставку на длину, а не на вычурный набор символов, и не навязывать пользователю обязательные спецсимволы — длинная фраза из обычных слов стойче к перебору и при этом легче запоминается4.
Так что на прямой вопрос «делает ли кто-то подобное» честный ответ: по духу — да, по частям. Скрытая последовательность без обратной связи — это port knocking. Пароль из слов — это парольная фраза. Есть и более экзотика: в патентах попадаются «многоуровневые матричные пароли» и схемы последовательной аутентификации, где итоговый вердикт выносится только после всех шагов5. Идея витает в воздухе.
Вопрос в другом: складывается ли из этих верных кусочков работающая защита от перебора. И вот тут начинаются проблемы.
Где идея ломается
Несколько шагов не добавляют ни бита стойкости. Это главное. Пароль «ёлка кофе мост», проверенный только в самом конце, ничем не отличается от одной строки «ёлкакофемост», по которой выносят единственный вердикт «да/нет». Стойкость к перебору определяется размером пространства, которое нужно прочесать, — а оно зависит от того, сколько и каких слов вы выбрали, а не от того, в скольких запросах их отправили. Разбили вы фразу на три пакета или послали одним — пространство поиска то же самое. Если слова слабые, слабой будет и вся комбинация, в один запрос её отправили или в восемь.
«Не понять, где ошибся» — это и так поведение нормальной системы. Мы только что про это говорили: правильный вход и без всякой многошаговости отвечает одним битом на весь пароль и не показывает, какой символ не сошёлся. Получается, многошаговая схема не добавляет это свойство — она лишь заново, дороже и сложнее, воспроизводит то, что и так есть по умолчанию.
Перебор тормозит совсем другое. Не число запросов. Атакующий не сидит за формой входа руками — он гоняет запросы скриптом, может слать их пачками, не дожидаясь ответа. Лишние шаги для скрипта почти ничего не стоят. По-настоящему перебор душат три вещи: ограничение числа попыток с растущей задержкой, блокировка после серии неудач и намеренно медленный хеш пароля — bcrypt, Argon2, — когда каждая проверка стоит атакующему ощутимых вычислений. NIST, к слову, рекомендует именно ограничение попыток как основную меру против перебора4. Многошаговый вход ни одну из этих трёх вещей не усиливает.
Серверу теперь надо помнить незаконченные попытки. Чтобы проверять слова по одному и молчать до конца, сервер обязан хранить состояние каждой начатой цепочки — в том числе брошенной на полпути или заведомо неверной. Это новая поверхность для атаки. Достаточно начинать миллионы цепочек и не доводить их до конца, чтобы засыпать сервер мусорным состоянием, — отказ в обслуживании получается почти бесплатно. У обычного пароля, который проверяется одним запросом без всякой памяти о попытке, этой проблемы просто нет.
Отказ от имени делает хуже, а не лучше. Имя пользователя — это не секрет, это указатель, по которому система находит нужную учётную запись. Уберите его — и пропадёт сама возможность считать попытки на конкретный аккаунт, заблокировать его, предупредить владельца о подозрительной активности. А ведь ограничение попыток на учётную запись — это и есть главная защита от перебора. Вдобавок появляется риск столкновений: если две цепочки слов совпали, второй человек войдёт в чужой аккаунт. Так что опознавание «только по паролю» не усиливает защиту, а выбивает из-под неё опору.
«Слова можно попроще» — самая опасная мысль во всей идее. Стойкость по-прежнему равна суммарной энтропии вашего секрета. Разбиение на шаги не выдаёт скидку на сложность — её просто неоткуда взять. Хуже того: вводить восемь слов подряд муторно, и пользователь под этим давлением выберет слова покороче и попроще, то есть собственными руками уменьшит ту самую энтропию, на которой всё держится.
И, наконец, удобство. Многошаговый вход — это лишние действия, больше шансов забыть слово или его порядок (исследования парольных фраз показывают, что сбои припоминания тут не редкость6), и совершенно неинформативный провал: опечатался в первом слове — впустую прошёл всю цепочку и не получил ни намёка, что не так. Принцип «молчим до конца», который задумывался как защита, по эту сторону экрана оборачивается раздражением.
Что решает ту же задачу
Если отбросить механику и оставить намерение — «сделать каждую догадку дороже и не дать атакующему обратной связи», — то у отрасли давно есть инструменты, которые бьют точно в цель:
- Медленный хеш — bcrypt, Argon2. Делает каждую проверку пароля вычислительно дорогой. Для одного честного входа это незаметно, для перебора миллионов вариантов — стена.
- Ограничение попыток и растущая задержка. После нескольких неудач аккаунт начинает отвечать всё медленнее или временно закрывается. Бьёт ровно по скорости цикла перебора.
- Второй фактор — одноразовые коды (TOTP). Добавляет к секрету ещё и привязку ко времени, которую не подберёшь словарём.
- Passkeys и WebAuthn. Самое радикальное: вместо общего секрета — пара ключей, и перебирать становится просто нечего.
Все они дают то, чего хотела многошаговая идея, — и без хранимого состояния, без отказа в обслуживании и без мучений на входе.
Вывод
Идея «пароль по словам» ценна не как готовая схема, а как способ самому, с нуля, вывести принципы, на которых стоит современная аутентификация: не выдавать, где ошибка; прятать сервис за секретной последовательностью; делать секрет длинным, а не вычурным. Всё это — настоящие, рабочие вещи, и приятно, что интуиция к ним выводит.
Но как замена паролю она не складывается. Число шагов не двигает ту единственную величину, которая определяет стойкость к перебору, — энтропию. А то, что она двигает — количество запросов, — перебор всё равно не останавливает: его останавливают медленный хеш и ограничение попыток. Там же, где скрытая последовательность действительно живёт в дикой природе, в port knocking, её используют не как пароль, а как дополнительный слой маскировки сервиса, да ещё и в связке с криптографией, а не вместо неё.
Timing attack — Wikipedia. https://en.wikipedia.org/wiki/Timing_attack ; разбор на примере имени пользователя: Brendan Long, «Timing attacks and usernames». https://www.brendanlong.com/timing-attacks-and-usernames.html ↩︎
Port knocking — Wikipedia. https://en.wikipedia.org/wiki/Port_knocking ↩︎
Single Packet Authorization. https://portguard.net/ ; Michael Rash, «Port Knocking and Single Packet Authorization». https://www.cipherdyne.org/ ↩︎
NIST Special Publication 800-63B, Digital Identity Guidelines. https://pages.nist.gov/800-63-4/sp800-63b.html ↩︎ ↩︎
Примеры из патентных заявок: «Multi-level matrix passwords» и «Sequential authentication using error rates». https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/10395015 ↩︎
Keith et al., «The usability of passphrases for authentication: An empirical field study». https://www.sciencedirect.com/science/article/abs/pii/S1071581906001236 ↩︎