// Безопасность OTP

Лучшие практики реализации OTP: коды, срок действия, лимиты и запасные каналы

Одноразовый код выглядит тривиально — сгенерируй число, отправь SMS, проверь. Но именно в деталях живут баги угона аккаунтов. Это руководство охватывает решения, которые делают OTP-поток по-настоящему безопасным и удобным: энтропия кода, хранение, окна срока действия, ограничение частоты, повторы и запасные каналы.

Генерируйте коды с настоящей энтропией

Используйте криптографически стойкий генератор случайных чисел, никогда — предсказуемое зерно или значение, выведенное из метки времени. Шесть цифр — обычный баланс безопасности и удобства; более короткие коды требуют строже ограничивать частоту, чтобы оставаться безопасными.

Никогда не отправляйте один и тот же код дважды и никогда не делайте код угадываемым из id пользователя или последовательности. Вся безопасность SMS-OTP держится на непредсказуемости кода в его короткий срок жизни.

  • Используйте CSPRNG (например, библиотеки secrets/crypto), а не PRNG общего назначения.
  • 6 цифр — стандарт; если используете 4, жёстко ужесточите лимиты и срок действия.
  • Один код на запрос; аннулируйте предыдущие коды при выпуске нового.

Храните коды безопасно, никогда — в открытом виде

Относитесь к OTP как к недолговечному паролю. Храните хеш кода (вместе с идентификатором и сроком действия), а не сырое значение, чтобы утечка базы не раскрыла живые коды. Сравнивайте функцией постоянного времени, чтобы избежать атак по времени, и удаляйте запись, как только код использован или истёк.

  • Хешируйте код перед хранением; сравнивайте за постоянное время.
  • Привязывайте каждый код к одному пользователю/сессии и назначению.
  • Удаляйте при успехе, истечении или достижении лимита попыток.

Задайте короткое окно срока действия

Коды должны жить минуты, а не часы. Срок 5–10 минут — типичный диапазон: достаточно долго, чтобы задержавшаяся SMS ещё дошла, и достаточно коротко, чтобы утёкший код обычно уже был мёртв. Показывайте пользователю оставшийся срок, чтобы медленное сообщение не читалось как сломанный поток.

!

Сочетайте короткий срок действия с задержкой повторной отправки, иначе пользователи будут долбить «отправить снова» при задержке сообщения и умножат ваши расходы на SMS.

Ограничивайте частоту по каждому измерению

Ограничение частоты — то, что останавливает и перебор кодов, и злоупотребление вашей конечной точкой отправки (что стоит вам денег и может привести к пометке отправителя). Ограничивайте по номеру телефона, по IP и по аккаунту, и на отправке, и на проверке.

  • Попытки проверки: ограничьте попытки на код (например, 5), затем аннулируйте и заставьте отправить заново.
  • Запросы отправки: задержка между отправками и суточный лимит на номер.
  • Глобальные ограждения: лимиты по IP и по аккаунту, чтобы притупить автоматизированное злоупотребление.
  • Добавьте экспоненциальную задержку или CAPTCHA при многократном достижении лимитов.

Обрабатывайте повторы и запасные каналы

SMS работает по принципу best-effort, поэтому хороший поток исходит из того, что часть сообщений не дойдёт. Предложите повтор после задержки, а после одной-двух неудачных попыток SMS эскалируйте на запасной канал — голосовой звонок, зачитывающий код, WhatsApp или e-mail — именно это автоматизируют управляемые Verify-API.

Сделайте состояния сбоя человечными: понятный путь «не пришло?», возможность сменить номер и контакт поддержки после повторных сбоев.

  1. 1

    Первая попытка: SMS

    Отправьте код и запустите видимый обратный отсчёт с задержкой повторной отправки.

  2. 2

    Повтор по запросу

    Разрешите повтор после задержки; аннулируйте предыдущий код при выпуске нового.

  3. 3

    Эскалируйте канал

    После повторного сбоя SMS предложите голос или альтернативный канал для той же верификации.

  4. 4

    Предложите выход

    Дайте пользователю исправить номер или обратиться в поддержку вместо тупика.

Улучшайте UX без ослабления безопасности

Мелочи повышают долю завершений: используйте WebOTP API и форматы автозаполнения SMS, чтобы мобильные браузеры могли прочитать код, держите формулировку сообщения одинаковой (некоторые платформы кешируют доверенных отправителей) и никогда не включайте в сообщение ничего, кроме кода и названия приложения. Избегайте ссылок в OTP-сообщениях — они приучают пользователей кликать и запускают спам-фильтры операторов.

  • Поддерживайте WebOTP / автозаполнение одноразового кода на мобильных.
  • Держите сообщение минимальным: название приложения + код, без маркетинга, без ссылок.
  • Локализуйте текст сообщения, но держите формат кода стабильным.

Частые вопросы

Сколько должен действовать OTP?+

Пять–десять минут — обычное окно. Достаточно, чтобы пережить задержавшуюся SMS, но коротко настолько, что утёкший код обычно уже истёк. Всегда показывайте оставшееся время, чтобы медленное сообщение не выглядело как сломанный поток.

Сколько попыток проверки разрешить?+

Ограничьте попытки на код примерно пятью, затем аннулируйте код и потребуйте новую отправку. Сочетайте это с лимитами по номеру, по IP и по аккаунту на отправке и проверке, чтобы остановить перебор и злоупотребление конечной точкой отправки.

Стоит ли класть ссылку в OTP-сообщение?+

Нет. Держите OTP-сообщения из одного лишь названия приложения и кода. Ссылки приучают кликать, могут утечь код через referrer и часто запускают спам-фильтрацию операторов, что вредит доставляемости.

Достаточно ли SMS-OTP безопасен сам по себе?+

SMS-OTP — заметный шаг вперёд по сравнению с одними паролями, но уязвим к SIM-swap и перехвату. Для ценных аккаунтов предлагайте более сильные факторы вроде passkey или TOTP и относитесь к SMS как к запасному — см. руководство «Альтернативы OTP».

EdgeGigs

Реализуйте OTP-поток правильно с первого раза.

Наймите проверенного разработчика на EdgeGigs, чтобы встроить в ваше веб- или мобильное приложение безопасную генерацию кодов, срок действия, ограничение частоты и запасные каналы.

Найти разработчика на EdgeGigs