# Пароль по словам: разбор идеи о многошаговой авторизации


Обычный вход устроен просто до примитивности. Есть поле для имени и поле для
пароля, вы заполняете оба, нажимаете кнопку — и за один запрос отправляете на
сервер всё сразу. Сервер сверяет и тут же отвечает: пустили или нет. Одна
попытка — один ответ, мгновенно.

Именно эта мгновенность и не даёт покоя. Раз каждая попытка возвращает сразу
понятный результат, её можно повторять машинально, тысячами в секунду. Так и
работает перебор: берём словарь, скармливаем его форме входа, ждём, когда
сервер скажет «да». Чем быстрее цикл «отправил — получил ответ», тем дешевле
атака.

Отсюда напрашивается идея, которую я и хочу разобрать. А что если не отправлять
пароль одним куском?

## Суть предложения

Пусть пароль — это не строка, а несколько отдельных «слов». Каждое слово
вводится и уходит на сервер само по себе, отдельным шагом. Вход превращается в
цепочку: слово, потом ещё слово, потом ещё. А ответ — пустили или нет —
приходит только в самом конце, на заранее условленном шаге. Скажем, после
третьего слова. Или после восьмого.

От имени пользователя при этом, возможно, можно вообще отказаться и опознавать
человека только по самой цепочке.

Логика за этим такая. Чтобы проверить одну догадку, переборщику теперь надо
отправить не один запрос, а целую серию. Промежуточные шаги молчат — нельзя
понять, на каком именно слове ты ошибся. А раз так, рассуждение продолжается,
то и сами слова не обязаны быть особенно сложными.

Идея красивая. И, что интересно, она нащупывает сразу несколько вещей, которые
в безопасности действительно важны. Но ровно в том месте, где она обещает
главную выгоду, она и разваливается. Давайте по порядку — сначала про то, что
угадано верно, потом про то, что нет.

## Что в этой идее угадано верно

**Не говорить, где именно ошибся.** Это не выдумка, а давний принцип. Хорошо
сделанный вход уже сейчас не сообщает, что неверно — имя или пароль. Он отвечает
одинаково расплывчато в обоих случаях, потому что любая подсказка сужает
переборщику пространство поиска. Больше того: грамотная проверка пароля сравнивает
строки за постоянное время, не прерываясь на первом несовпавшем символе.
Иначе появляется тонкая лазейка — атака по времени ответа: по тому, насколько
быстро сервер сказал «нет», можно угадывать пароль по одному символу[^timing].
Так что «не выдавать, где ошибка» — это правильный инстинкт. Запомним его, он
ещё пригодится.

**Секретная последовательность как условие доступа.** Это тоже не новость — это
буквально существует и называется *port knocking*, «стук в порты». Сервис
прячется за наглухо закрытым межсетевым экраном и открывает порт только тому,
кто постучал в заранее условленную последовательность портов. Пока вся
последовательность не пройдена правильно, снаружи всё выглядит закрытым, и —
ключевой момент — сервер ничего не подтверждает на промежуточных шагах, он
вообще не отвечает[^knock]. Есть и более зрелый вариант, *single packet
authorization*, где весь «стук» упакован в один зашифрованный пакет с подписью
и меткой времени[^spa]. То есть идея «пройди скрытую цепочку, и только тогда
получишь доступ» в мире существует и работает.

**Пароль из нескольких слов.** И это давно живёт под именем «парольная фраза».
Современные рекомендации, в том числе NIST, прямо советуют делать ставку на
длину, а не на вычурный набор символов, и не навязывать пользователю обязательные
спецсимволы — длинная фраза из обычных слов стойче к перебору и при этом легче
запоминается[^nist].

Так что на прямой вопрос «делает ли кто-то подобное» честный ответ: по духу —
да, по частям. Скрытая последовательность без обратной связи — это port
knocking. Пароль из слов — это парольная фраза. Есть и более экзотика:
в патентах попадаются «многоуровневые матричные пароли» и схемы
последовательной аутентификации, где итоговый вердикт выносится только после
всех шагов[^patents]. Идея витает в воздухе.

Вопрос в другом: складывается ли из этих верных кусочков работающая защита от
перебора. И вот тут начинаются проблемы.

## Где идея ломается

**Несколько шагов не добавляют ни бита стойкости.** Это главное. Пароль
«ёлка кофе мост», проверенный только в самом конце, ничем не отличается от одной
строки «ёлкакофемост», по которой выносят единственный вердикт «да/нет».
Стойкость к перебору определяется размером пространства, которое нужно
прочесать, — а оно зависит от того, сколько и каких слов вы выбрали, а не от
того, в скольких запросах их отправили. Разбили вы фразу на три пакета или
послали одним — пространство поиска то же самое. Если слова слабые, слабой будет
и вся комбинация, в один запрос её отправили или в восемь.

**«Не понять, где ошибся» — это и так поведение нормальной системы.** Мы только
что про это говорили: правильный вход и без всякой многошаговости отвечает
одним битом на весь пароль и не показывает, какой символ не сошёлся. Получается,
многошаговая схема не добавляет это свойство — она лишь заново, дороже и
сложнее, воспроизводит то, что и так есть по умолчанию.

**Перебор тормозит совсем другое.** Не число запросов. Атакующий не сидит за
формой входа руками — он гоняет запросы скриптом, может слать их пачками, не
дожидаясь ответа. Лишние шаги для скрипта почти ничего не стоят. По-настоящему
перебор душат три вещи: ограничение числа попыток с растущей задержкой,
блокировка после серии неудач и намеренно медленный хеш пароля — bcrypt,
Argon2, — когда каждая проверка стоит атакующему ощутимых вычислений. NIST,
к слову, рекомендует именно ограничение попыток как основную меру против
перебора[^nist]. Многошаговый вход ни одну из этих трёх вещей не усиливает.

**Серверу теперь надо помнить незаконченные попытки.** Чтобы проверять слова по
одному и молчать до конца, сервер обязан хранить состояние каждой начатой
цепочки — в том числе брошенной на полпути или заведомо неверной. Это новая
поверхность для атаки. Достаточно начинать миллионы цепочек и не доводить их до
конца, чтобы засыпать сервер мусорным состоянием, — отказ в обслуживании
получается почти бесплатно. У обычного пароля, который проверяется одним
запросом без всякой памяти о попытке, этой проблемы просто нет.

**Отказ от имени делает хуже, а не лучше.** Имя пользователя — это не секрет,
это указатель, по которому система находит нужную учётную запись. Уберите его —
и пропадёт сама возможность считать попытки на конкретный аккаунт, заблокировать
его, предупредить владельца о подозрительной активности. А ведь ограничение
попыток на учётную запись — это и есть главная защита от перебора. Вдобавок
появляется риск столкновений: если две цепочки слов совпали, второй человек
войдёт в чужой аккаунт. Так что опознавание «только по паролю» не усиливает
защиту, а выбивает из-под неё опору.

**«Слова можно попроще» — самая опасная мысль во всей идее.** Стойкость по-прежнему
равна суммарной энтропии вашего секрета. Разбиение на шаги не выдаёт скидку на
сложность — её просто неоткуда взять. Хуже того: вводить восемь слов подряд
муторно, и пользователь под этим давлением выберет слова покороче и попроще,
то есть собственными руками уменьшит ту самую энтропию, на которой всё держится.

**И, наконец, удобство.** Многошаговый вход — это лишние действия, больше
шансов забыть слово или его порядок (исследования парольных фраз показывают, что
сбои припоминания тут не редкость[^passphrase]), и совершенно неинформативный
провал: опечатался в первом слове — впустую прошёл всю цепочку и не получил ни
намёка, что не так. Принцип «молчим до конца», который задумывался как защита,
по эту сторону экрана оборачивается раздражением.

## Что решает ту же задачу

Если отбросить механику и оставить намерение — «сделать каждую догадку дороже и
не дать атакующему обратной связи», — то у отрасли давно есть инструменты,
которые бьют точно в цель:

- **Медленный хеш — bcrypt, Argon2.** Делает каждую проверку пароля вычислительно
  дорогой. Для одного честного входа это незаметно, для перебора миллионов
  вариантов — стена.
- **Ограничение попыток и растущая задержка.** После нескольких неудач аккаунт
  начинает отвечать всё медленнее или временно закрывается. Бьёт ровно по
  скорости цикла перебора.
- **Второй фактор — одноразовые коды (TOTP).** Добавляет к секрету ещё и
  привязку ко времени, которую не подберёшь словарём.
- **Passkeys и WebAuthn.** Самое радикальное: вместо общего секрета — пара
  ключей, и перебирать становится просто нечего.

Все они дают то, чего хотела многошаговая идея, — и без хранимого состояния,
без отказа в обслуживании и без мучений на входе.

## Вывод

Идея «пароль по словам» ценна не как готовая схема, а как способ самому, с нуля,
вывести принципы, на которых стоит современная аутентификация: не выдавать, где
ошибка; прятать сервис за секретной последовательностью; делать секрет длинным,
а не вычурным. Всё это — настоящие, рабочие вещи, и приятно, что интуиция к ним
выводит.

Но как замена паролю она не складывается. Число шагов не двигает ту единственную
величину, которая определяет стойкость к перебору, — энтропию. А то, что она
двигает — количество запросов, — перебор всё равно не останавливает: его
останавливают медленный хеш и ограничение попыток. Там же, где скрытая
последовательность действительно живёт в дикой природе, в port knocking, её
используют не как пароль, а как дополнительный слой маскировки сервиса, да ещё и
в связке с криптографией, а не вместо неё.

[^timing]: 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>
[^knock]: Port knocking — Wikipedia. <https://en.wikipedia.org/wiki/Port_knocking>
[^spa]: Single Packet Authorization. <https://portguard.net/>; Michael Rash, «Port Knocking and Single Packet Authorization». <https://www.cipherdyne.org/>
[^nist]: NIST Special Publication 800-63B, Digital Identity Guidelines. <https://pages.nist.gov/800-63-4/sp800-63b.html>
[^patents]: Примеры из патентных заявок: «Multi-level matrix passwords» и «Sequential authentication using error rates». <https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/10395015>
[^passphrase]: Keith et al., «The usability of passphrases for authentication: An empirical field study». <https://www.sciencedirect.com/science/article/abs/pii/S1071581906001236>

