Изнанка веб-безопасности: как сайты хранят ваши OTP-ключи (и хранят ли?)

Author
Анна Петрова
Senior UX Designer @ TechCorp

Новости
3.8 / 5 (84 оценок)


В современном цифровом мире двухфакторная аутентификация (2FA) стала практически стандартом де‑факто для защиты учетных записей. Одноразовые пароли (OTP, One-Time Password) — один из самых популярных способов реализации второго фактора. Мы ежедневно вводим коды из SMS, писем или приложений‑аутентификаторов, редко задумываясь о том, что происходит с этими цифрами «за кулисами». Знает ли сервер, какой именно код мы только что ввели? Хранятся ли где‑то наши OTP‑ключи? Если да, то как именно — в открытом виде, зашифрованными, или же применяются более хитрые схемы? Ответы на эти вопросы лежат в основе безопасности всей системы: неправильное хранение OTP может свести на нет все преимущества двухфакторной защиты. В этом материале мы подробно разберем «изнаночную» сторону веб‑безопасности, связанную с одноразовыми паролями, и выясним, какие методы хранения используют сайты и сервисы (и используют ли вообще).


One-Time Password и веб-безопасность

Что такое OTP и как он работает?

OTP (One-Time Password) — это пароль, который действителен только для одного сеанса аутентификации или транзакции. Главная его особенность — ограниченный срок жизни и невозможность повторного использования. Одноразовые пароли бывают разных типов: по способу генерации их делят на HOTP (HMAC‑based One-Time Password, счетчик событий) и TOTP (Time‑based One-Time Password, привязка ко времени). По каналу доставки — SMS, электронная почта, push‑уведомления, а также специальные приложения (Google Authenticator, Authy) и аппаратные токены (YubiKey). В любом случае цель одна: подтвердить, что пользователь имеет доступ к чему‑то, кроме пароля (телефону, email, устройству).

С технической точки зрения, процесс выглядит так: сервер генерирует или согласовывает с клиентом некоторое значение, которое пользователь должен ввести. При использовании TOTP и клиент, и сервер знают общий секрет (seed) и текущее время, поэтому могут независимо вычислить одинаковый код. При отправке кода по SMS сервер генерирует случайное число, отправляет его пользователю и ожидает, что тот введет его в форму. Здесь уже сервер обязан где‑то запомнить сгенерированное значение до момента его ввода или истечения срока жизни.

Хранят ли серверы OTP‑ключи? Генерация vs. хранение

Ответ на этот вопрос не так однозначен, как может показаться. Всё зависит от реализации и типа OTP. Если мы говорим о TOTP (как в Google Authenticator), то сервер вообще не хранит одноразовые коды. Вместо этого при настройке двухфакторной аутентификации генерируется секретный ключ, который передаётся пользователю (обычно в виде QR‑кода) и сохраняется на сервере в защищённом виде. Когда пользователь вводит текущий 6‑значный код, сервер самостоятельно вычисляет ожидаемое значение на основе этого секрета и текущего времени и сравнивает с введённым. То есть код существует только в момент проверки, в базе данных его нет. Исключение — ситуации, когда сервер логирует все попытки входа (что является плохой практикой).

Совсем иначе обстоит дело с OTP, отправляемыми по SMS или email. В этом случае сервер генерирует случайный код, должен его где‑то сохранить (пусть и временно), чтобы потом сверить с тем, что введёт пользователь. Здесь возникает необходимость хранения. Но опять же, это хранение временное — код живёт обычно от 30 секунд до 10–15 минут. Однако даже временное хранение требует соблюдения мер безопасности: коды не должны храниться в открытом виде, а после использования или истечения срока должны безвозвратно удаляться. На практике так происходит не всегда, и многие сайты по небрежности или из‑за архитектурных ограничений оставляют OTP в логах или базах данных дольше необходимого.

Методы хранения OTP на стороне сервера: от плохих к хорошим

Рассмотрим, как именно могут храниться одноразовые пароли (когда хранение необходимо). Здесь можно провести аналогию с хранением обычных паролей, но с нюансами: OTP живут недолго, но если злоумышленник получит доступ к базе данных, где лежат неиспользованные коды, он сможет выдать себя за любого пользователя. Поэтому подходы к хранению должны быть особенно тщательными.

  • Хранение в открытом виде (plaintext). Самый опасный метод. База данных или, что ещё хуже, текстовые логи содержат сгенерированные коды как есть. При любой утечке злоумышленник получает полный контроль над аккаунтами, для которых эти коды ещё активны. К сожалению, даже крупные компании иногда допускают такую ошибку — например, оставляя OTP в логах отладки.
  • Хеширование (с солью). Более безопасный способ — хранить не сам код, а его хеш (например, bcrypt, SHA‑256 с солью). При проверке сервер хеширует введённый пользователем код и сравнивает с сохранённым хешем. Однако у этого подхода есть проблема: OTP обычно короткие (4–8 цифр), что делает их уязвимыми для быстрого перебора хешей (радужные таблицы не нужны, можно просто захешировать все возможные комбинации). Поэтому обязательна соль и, желательно, замедление (как у bcrypt). Но главный минус — хеш нельзя использовать для алгоритмов, где сервер должен сам вычислить код (как в TOTP). Кроме того, хеш всё равно хранится в базе, и если база утекла, злоумышленник может попытаться подобрать код офлайн, особенно если коды короткие.
  • Шифрование с обратимостью. Некоторые системы шифруют OTP перед сохранением (AES, например). Это лучше, чем plaintext, но создаёт дополнительные риски: ключ шифрования обычно лежит там же, на сервере (в конфигурации или в памяти), и если злоумышленник получит доступ к серверу целиком, он сможет расшифровать все коды. При утечке только базы данных (без доступа к ключам) шифрование защищает, но не всегда.
  • Временное хранение в оперативной памяти (in‑memory store). Оптимальный способ для кодов, отправляемых по SMS/email — хранить их только в быстрой энергозависимой памяти, например в Redis или Memcached, с установленным сроком жизни (TTL). При этом сами значения тоже желательно хранить в захешированном виде или в зашифрованном. Если используется Redis, можно комбинировать: ключом может быть идентификатор пользователя или сессии, а значением — хеш кода. После истечения TTL запись автоматически удаляется. Такой подход минимизирует риск утечки на диск и упрощает очистку.
  • Отсутствие хранения (stateless подход). Для TOTP и подобных алгоритмов можно вообще не хранить коды. Сервер вычисляет их на лету. Для SMS‑кодов тоже существуют stateless методы: например, можно генерировать код и сразу отправлять его пользователю, а затем, вместо сохранения в БД, формировать подписанный токен (JWT), содержащий этот код и срок действия. Токен отдаётся клиенту (например, в куки или скрытом поле формы) и при повторной отправке формы проверяется. Код при этом нигде на сервере не хранится. Однако этот метод требует осторожности с передачей токена клиенту и его защитой от перехвата.

На практике выбор метода зависит от требований к безопасности, сложности реализации и законодательных норм. В идеале разработчики должны сочетать несколько уровней защиты: хранить коды только в памяти, с TTL, в захешированном виде, а также использовать механизмы rate limiting для предотвращения подбора.

Секреты для генерации OTP против самих одноразовых кодов

Важно различать два типа данных: секреты (seed) для алгоритмов TOTP/HOTP и сами сгенерированные одноразовые коды. Секреты обычно устанавливаются один раз при включении 2FA и хранятся постоянно. Именно они позволяют серверу и клиенту (например, приложению‑аутентификатору) генерировать одинаковые последовательности кодов. Если секрет скомпрометирован, злоумышленник сможет генерировать все будущие OTP для данного аккаунта, пока пользователь не сменит секрет. Поэтому хранение секретов должно быть максимально надёжным: они шифруются с использованием мастер‑ключа или хранятся в аппаратных модулях безопасности (HSM).

Сами же одноразовые коды (те самые 6 цифр, которые мы вводим) — это эфемерные данные. При правильной TOTP‑реализации сервер их вообще не хранит, а вычисляет при проверке. При отправке по SMS код хранится временно и должен быть уничтожен после использования или истечения срока. Смешение этих двух понятий часто приводит к ошибкам: например, некоторые разработчики хранят в базе данных последний сгенерированный SMS‑код в открытом виде рядом с профилем пользователя — это грубое нарушение безопасности.

Как хранятся OTP в разных сценариях (SMS, TOTP, email, push)

Рассмотрим популярные сценарии использования OTP и типичные способы их обработки на стороне сервера.

  • SMS‑коды. Самый распространённый, но и самый уязвимый метод. Сервер генерирует случайное число (обычно 4–6 цифр), отправляет его через SMS‑шлюз и должен сохранить это число до момента проверки. Хорошие практики: хранение в зашифрованном виде в оперативной памяти с TTL, использование соли и хеширования. Плохие практики: хранение в открытом виде в колонке базы данных `sms_code`, отсутствие TTL, логирование кода в файлах.
  • Email‑коды. Аналогичны SMS, но код отправляется по электронной почте. Здесь добавляется риск перехвата письма, но с точки зрения серверного хранения ситуация та же: требуется временное сохранение кода для последующей проверки. Часто email‑коды используются для восстановления доступа, и здесь особенно важно не хранить их дольше необходимого.
  • TOTP (приложения‑аутентификаторы). Как уже говорилось, сервер не хранит коды. Он хранит секрет, защищённый шифрованием, и при проверке вычисляет текущий код по алгоритму TOTP (RFC 6238). Это наиболее безопасный подход, так как секрет редко используется (только при проверке), а сами коды не покидают устройство пользователя и сервер.
  • Push‑уведомления с одноразовым кодом или подтверждением. В этом случае сервер может отправить push‑уведомление, а пользователь нажимает «Подтвердить» в приложении. Здесь код как таковой может вообще не генерироваться — вместо этого используется криптографически подписанный запрос. Однако иногда в push‑уведомлении всё же содержится код, и тогда он должен обрабатываться так же, как SMS.
  • Аппаратные токены (YubiKey и подобные). При использовании U2F/WebAuthn сервер не хранит никаких OTP, вместо этого используются асимметричные ключи. В legacy‑режиме YubiKey может эмулировать ввод одноразового пароля, и тогда сервер должен иметь в своей базе запись о секрете токена, чтобы проверить этот пароль — что опять возвращает нас к защищённому хранению секретов.

В таблице ниже представлено сравнение методов хранения в разных сценариях.

СценарийЧто хранится на сервере постоянно?Хранятся ли сами OTP?Рекомендуемый метод
SMS/Emailнет (только временные данные)да, временноХеш в Redis с TTL
TOTPсекрет (seed)нетШифрование секрета
HOTPсекрет + счётчикнетШифрование секрета и счётчика
Push‑2FAпубличный ключ устройстванетХранение ключей

Риски и реальные утечки: что попадает в логи и базы данных

Несмотря на все теоретические выкладки, реальность часто далека от идеала. Самая частая проблема — логирование. Разработчики включают подробное логирование на этапе отладки и забывают отключить его на проде. Можно купить otp в виде готового софта, где каждый сгенерированный OTP будет аккуратно записывается в текстовый файл (или в базу данных логов) вместе с идентификатором пользователя и временной меткой. Если логи утекают (например, из‑за неправильно настроенного S3‑бакета или SQL‑инъекции), все когда‑либо использованные коды становятся доступны злоумышленнику. Хотя срок действия большинства кодов уже истёк, это даёт информацию о паттернах поведения и может помочь в атаках социальной инженерии.

Другой риск — хранение кодов в основных базах данных в незащищённом виде. Иногда можно встретить таблицу users с колонкой `sms_code`, где лежит последний отправленный код, и `sms_code_expires`. Если злоумышленник получит дамп базы данных, он сможет для любого пользователя узнать текущий активный код и войти в аккаунт. Особенно опасно, если коды не очищаются после успешного входа или истечения срока — они остаются в базе годами.

Известны случаи утечек баз данных крупных сервисов, где исследователи находили миллионы записей с открытыми OTP. В 2021 году, например, была обнаружена открытая база данных одного из провайдеров 2FA‑сервисов, содержащая секреты и коды в незашифрованном виде. Это позволило бы злоумышленникам генерировать одноразовые пароли для любых аккаунтов.

Регуляторные требования и стандарты безопасности

Хранение OTP и секретов двухфакторной аутентификации подпадает под действие многих регуляторных требований, особенно в финансовом секторе и сфере персональных данных. Рассмотрим основные.

  • PCI DSS (Payment Card Industry Data Security Standard). Требует обязательного использования двухфакторной аутентификации для доступа к данным держателей карт. Хотя стандарт не предписывает конкретного способа хранения OTP, он требует защиты всех аутентификационных данных. Секреты должны храниться с использованием сильной криптографии.
  • GDPR (General Data Protection Regulation). Не содержит прямых указаний на OTP, но требует защиты персональных данных. Утечка OTP может привести к компрометации аккаунтов и, как следствие, утечке персональных данных, что грозит крупными штрафами.
  • NIST Special Publication 800-63B (Digital Identity Guidelines). Содержит подробные рекомендации по аутентификации. В частности, NIST рекомендует не использовать SMS в качестве второго фактора из‑за его уязвимостей, а при использовании OTP хранить их с применением надёжной криптографии и ограничивать время жизни. Также рекомендуется использовать TOTP с секретами, защищёнными аппаратно.
  • ФЗ-152 (Россия) и аналогичные законы других стран. Требуют защиты персональных данных, к которым относятся и логины, и пароли, и OTP при их привязке к конкретному лицу.

Соблюдение этих стандартов на практике означает, что компания обязана документировать, как именно хранятся OTP, проводить аудит безопасности и принимать меры по предотвращению утечек.

Лучшие практики для разработчиков: как хранить (или не хранить) OTP

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

  1. Не храните OTP дольше необходимого. Устанавливайте минимально возможный срок жизни (TTL) и удаляйте код сразу после успешной проверки или истечения времени.
  2. Никогда не храните OTP в открытом виде. Используйте хеширование (с солью) или шифрование. Для кратковременного хранения подойдёт хеш с солью, при этом важно использовать алгоритмы, устойчивые к быстрому перебору (bcrypt, PBKDF2, Argon2).
  3. Отдавайте предпочтение хранению в оперативной памяти. Redis или Memcached с настроенным TTL — идеальный выбор. Базы данных на диске (даже с TTL) увеличивают риск оставления данных на физических носителях.
  4. Используйте stateless подход, где это возможно. Например, можно не хранить код на сервере, а отдавать его клиенту в подписанном токене, который клиент обязан предъявить при подтверждении. Однако этот токен нужно защищать от перехвата и подделки.
  5. Защищайте секреты для TOTP/HOTP. Они должны храниться в зашифрованном виде, а ключи шифрования — отдельно от базы данных (например, в HSM или секретных менеджерах).
  6. Не логируйте OTP. Ни в коем случае не пишите сгенерированные или введённые коды в логи. Если нужно логировать факт отправки, записывайте только факт (например, «код отправлен пользователю X») без самого кода.
  7. Внедрите rate limiting и защиту от перебора. Даже если коды хранятся безопасно, злоумышленник может попытаться подобрать код через форму ввода. Ограничение числа попыток и задержки между ними обязательны.
  8. Проводите регулярные аудиты кода и тесты на проникновение. Ищите места, где OTP могут оказаться в логах или в открытом виде в БД.

Следование этим правилам позволит минимизировать риски даже в случае частичной компрометации системы.

Что может сделать пользователь для своей безопасности?

Хотя основная ответственность лежит на разработчиках, пользователи тоже могут предпринять шаги, чтобы снизить вероятность компрометации своих OTP.

  • По возможности используйте приложения‑аутентификаторы (TOTP) вместо SMS. Как мы выяснили, TOTP исключает хранение кодов на сервере и снижает риск их перехвата.
  • Включите двухфакторную аутентификацию везде, где это возможно. Даже неидеальная 2FA лучше, чем её отсутствие.
  • Используйте отдельные приложения для 2FA. Google Authenticator, Authy, Aegis (Android) или Raivo OTP (iOS) — лучше, чем получать коды по SMS, особенно если ваш номер телефона может быть подвергнут SIM‑свопингу.
  • Обращайте внимание на подозрительные задержки или сообщения. Если вы запросили код, но не получили его, или получили неожиданное уведомление о входе — это повод насторожиться.
  • Не вводите OTP на подозрительных сайтах. Фишинговые сайты могут маскироваться под страницы ввода кода, и даже самый безопасный сервер не спасёт, если вы сами отдали код злоумышленнику.
  • Используйте менеджеры паролей с поддержкой TOTP. Многие современные менеджеры (Bitwarden, 1Password, KeePassXC) могут хранить секреты TOTP и генерировать коды, что удобно и безопасно (если вы доверяете своему менеджеру).

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

Технологии не стоят на месте, и мы видим постепенный отказ от SMS в пользу более защищённых протоколов (WebAuthn, passkeys). Однако OTP в том или ином виде будут существовать ещё долго, и понимание их «изнанки» остаётся критически важным для всех участников цифрового взаимодействия.


#UX #UI #Дизайн #Доступность
Author

Анна Петрова

Senior UX Designer @ TechCorp

Более 10 лет опыта в дизайне пользовательских интерфейсов. Работала с компаниями из списка Fortune 500. Спикер на международных конференциях по дизайну.

Комментарии (5)

Оставить комментарий

Ваш email не будет опубликован

М
Михаил Сидоров

15 Января 2026

Отличная статья! Особенно понравился раздел про визуальную иерархию. Обязательно применю эти принципы в своём следующем проекте.

Е
Елена Козлова

20 Декабря 2025

Спасибо за подробное объяснение принципов доступности. Многие дизайнеры недооценивают эту тему, а зря!

А
Алексей Новиков

17 Ноября 2025

Could you share more about design systems? Would love to see a dedicated article on that topic!

Понравилась статья?

Подпишитесь на нашу рассылку и получайте новые материалы каждую неделю