Одноразовые коды могут быть абсолютно правильными на вашем телефоне, и всё же сервер отклонит их в ту же секунду. Обе стороны вычисляют одну и ту же функцию из общего секрета и текущего шага времени, поэтому расхождение во времени даёт расхождение в коде. Из-за этого дрейф часов становится не просто неудобством, а неисправностью, значимой для безопасности.

Здесь важны двое часов, и исправление видимых — это то, что люди делают первым. Те, которым обычно нужно внимание, — другие.

Код — это функция времени, а не пароль

TOTP берёт общий секрет и счётчик, производный от текущего времени, пропускает оба через хеш с ключом и усекает результат до короткой десятичной строки, как указано в RFC 6238. Счётчик продвигается на каждом шаге времени, обычно на тридцать секунд, поэтому один и тот же секрет даёт новое значение при смене шага. Проверяющая сторона пересчитывает ожидаемое значение для текущего шага и сравнивает, что требует того же секрета и того же представления о настоящем. Пароль остаётся пригодным для повторного использования, пока его не сменят, а это значение истекает на границе, которую клиент не видит.

Поэтому один код работает, а следующий терпит неудачу через мгновение. Это же объясняет, почему копирование кода из сообщения в чате — такая ненадёжная привычка.

Двое часов должны совпадать, а не одни

Материал регистрации — это первое общее, а время — второе. Если секрет верен, а часы расходятся больше допустимого окна, каждое сгенерированное значение попадает в неверный шаг и отклоняется. Если часы совпадают, а секрет неверен, каждое значение тоже отклоняется, и оба сбоя выглядят одинаково на экране входа. Разделить их — значит проверять запись учётной записи и время независимо, именно в таком порядке.

Часы проверяющей стороны заслуживают такой же проверки, как и часы устройства. Сервер с неисправным источником времени отклоняет правильные коды всех пользователей сразу, и в отчётах будет описана внезапная авария, а не дрейф. Этот образец стоит распознавать, потому что его исправление относится к серверу, а не к сотне телефонов.

Чего стоит расширение окна

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

Исправление часов — это ремонт, а расширение окна — успокоительное. Первое устраняет причину, второе её скрывает.

Проверьте оба часов перед повторной попыткой

Прочитайте время на устройстве и на сервере и сравните смещения, а не отображаемое местное время. Приведённые ниже команды показывают состояние синхронизации и необработанный момент UTC на хосте или сервере Linux. Выполните их на обоих концах, прежде чем менять какие-либо параметры аутентификации.

timedatectl
date -u +'%Y-%m-%dT%H:%M:%SZ'
chronyc tracking
timedatectl timesync-status

Смещение в секундах безвредно, смещение в минутах объясняет отказ, а система, не сообщающая ни об одном источнике синхронизации, — кандидат на оба. Сравните две метки времени UTC напрямую, поскольку местное время включает часовой пояс, которого алгоритм никогда не видит. Если смещения малы, а коды всё ещё не работают, прекратите настраивать часы и вместо этого проверьте запись регистрации. Повторные попытки против заблокированной учётной записи затрудняют наблюдение исходной неисправности.

Запишите время отказа, смещение и текст ошибки, а общий секрет оставьте вне этого. Поддержка может работать с первыми тремя и никогда не должна видеть четвёртое.

Похожие статьи