Почему действительный одноразовый код всё равно не срабатывает при входе в VPN
Отклонённый код — это обычно проблема порядка запросов, регистрации или времени, а не неверный код. Проверьте эти пункты по порядку, прежде чем что-то перерегистрировать.
Вы открываете клиент VPN, вводите пароль, и шесть цифр из аутентификатора возвращаются как недействительные — дважды, затем в третий раз. В учётной записи с вчерашнего дня ничего не изменилось, поэтому первый порыв — удалить запись в аутентификаторе и отсканировать её заново — обычно это неверное исправление. Одноразовый код — не второй пароль: шлюз должен запросить его на определённом шаге, из определённой зарегистрированной записи, в определённом окне, и большинство отказов происходит до сравнения цифр. Определите, на каком вы шаге, прежде чем трогать регистрацию.
Порядок запросов показывает, в каком потоке вы находитесь
Три формы покрывают почти любой вход в VPN с кодом. В двухшаговой форме клиент отправляет пароль, сервер выдаёт вызов, и клиент спрашивает снова. В комбинированной форме шлюз хочет пароль и код в одной отправке — иногда склеенными, иногда после разделителя, который документирует только оператор. В браузерной форме у клиента вообще нет поля: код вводится на веб-странице, либо push-подтверждение заменяет его, а настольный клиент ждёт результата.
Неверная форма выглядит как плохой код. Вставьте свежий код в поле только для пароля, и сервер прочитает его как часть пароля. На стороне клиента OpenVPN 2.4 и новее делают разницу видимой: сервер, отвечающий AUTH_FAILED,CRV1:, ожидает ответ на вызов, поэтому профилю нужен static-challenge или файл пароля в форме SCRV1:. Более старый профиль только с auth-user-pass проверяет пароль и затем останавливается, что в журнале выглядит как сбой OTP, хотя код не отправлялся.
WireGuard стоит назвать исключением: он аутентифицирует пары ключей, а не людей, поэтому поля для кода нет. Если провайдер рекламирует такой код, его принимает что-то за пределами туннеля.
Что сервер делает с шестью цифрами
Коды на основе времени следуют RFC 6238, который оборачивает более старую конструкцию со счётчиком из RFC 4226: HMAC над движущимся фактором, по умолчанию усечённый до шести цифр. Для TOTP движущийся фактор — текущее время Unix, делённое на шаг, 30 секунд, если оператор его не менял. Проверяющая сторона никогда не сравнивает ваши цифры с сохранённым значением; она пересчитывает ожидаемый код из общего зерна и обычно принимает соседние шаги, поэтому медленный обмен не срывает вход.
Два следствия упускаются. Часовой пояс телефона не имеет значения, потому что зерно никогда не видит местные часы — важна только отстройка от реального UTC, и аппарат, спешащий на две минуты, ошибается на каждом коде, показывая при этом чистый обратный отсчёт. И код одноразовый по замыслу: проверяющая сторона помнит последнее принятое значение счётчика для этого зерна, поэтому те же цифры, отправленные снова, отклоняются даже в том же окне. Клиент, повторяющий попытку после тайм-аута, может сжечь только что отправленный код.
Токены на основе счётчика сдвигают счётчик при каждом нажатии, поэтому выходят из синхронизации после того, как коды сгенерированы, но так и не отправлены; обычное исправление — окно ресинхронизации, принимающее несколько значений вперёд. Если оператор зарегистрировал HOTP, а ваше приложение показывает TOTP, каждое значение неверно так, что это похоже на проблему с часами. URI подготовки за QR-кодом указывает тип, цифры, период и хеш.
Сбои, которые выглядят как неверный код
Самая частая причина — зарегистрированная запись. Приложения-аутентификаторы помечают записи по издателю и учётной записи, поэтому у кого две личности на одном VPN, может оказаться две записи, метки которых различаются одним символом. Код действителен для зерна, которое его сгенерировало, и ни для чего другого. Сравните метку на телефоне с именем пользователя, которое фиксирует сервер, а не с тем, что, как вам помнится, вы ввели.
Второе — несовпадающий набор параметров. Зерно, зарегистрированное с восемью цифрами, периодом 60 секунд или SHA-256, даёт коды, которые проверяющая сторона, ожидающая шесть цифр, 30 секунд и SHA-1, отклоняет, а на клиенте это неотличимо от неверного кода. Это проявляется после миграций, когда запись добавляли заново по фотографии QR-кода или когда запись токена пересобирали со значениями по умолчанию.
Третье — не про код. Шлюзы обычно возвращают одну общую ошибку аутентификации для неверного пароля, истёкшего кода и отключённой учётной записи, намеренно, чтобы никто не мог выяснить, какой фактор отказал. Читайте журнал шлюза или RADIUS вместо сообщения клиента: никогда не выданный вызов, отказ в доступе без атрибута OTP и отказ по политике выглядят на экране одинаково и ведут к разным следующим шагам.
Короткая последовательность, которая это сужает
Начните с самой дешёвой проверки и остановитесь на первом шаге, который не выполняется.
- Дождитесь свежего кода, отправьте его один раз и отметьте, появляется ли вызов до или после принятия пароля.
- Сопоставьте метку в аутентификаторе с именем пользователя, которое хранит сервер.
- Убедитесь, что профиль вообще может ответить на вызов, как в случае
CRV1выше. - Сравните часы устройства с известным источником времени; расхождение больше нескольких десятков секунд само ломает TOTP.
- Спросите оператора, TOTP это или HOTP и какие цифры, период и хеш были зарегистрированы.
Два правила не дают этому ухудшиться. Не повторяйте попытки много раз: многие шлюзы считают сбои на учётную запись и либо блокируют её, либо начинают задерживать ответы, так что шквал превращает мелкую ошибку конфигурации в обращение в поддержку. И не перерегистрируйте токен ради диагностики, потому что это делает старое зерно недействительным; если настоящей неисправностью был вызов, который клиент так и не отправил, теперь у вас две изменения для объяснения вместо одного.
Держите зерно и коды восстановления вне цикла
Шесть цифр и секрет за ними не взаимозаменяемы. QR-код подготовки или зерно Base32 генерирует каждый будущий код учётной записи, поэтому оно относится к той же категории, что и пароль: не на снимке экрана, не в комментарии к обращению, не в переписке с тем, кто первым связался с вами. Коды восстановления — отдельный механизм: одноразовые, часто полностью пропускают запрос кода, а потратив один на проверку учётной записи, вы убираете запасной путь, который может понадобиться позже.
Код также не делает страницу входа заслуживающей доверия: запрос OTP на похожем домене собирает действительный код и повторяет его в пределах окна, а сертификат по-прежнему говорит, куда вы вводите.
Если последовательность заканчивается на профиле, который не может ответить на вызов, исправьте профиль и проверьте снова одним свежим кодом. Если она упирается в несовпадающие параметры регистрации, попросите оператора прочитать запись токена, прежде чем кто-либо что-то отсканирует. Если она упирается в блокировку, дождитесь её окончания, а не меняйте клиенты — препятствие в состоянии учётной записи, а не в приложении.