Представим вполне обычную задачу. В продукте нужно проверить восстановление пароля.

Пользователь вводит email, получает письмо, переходит по ссылке и задаёт новый пароль. Ссылка действует час и после использования должна перестать работать. Никакой экзотики. Такую форму мы видели сотни раз.

Тестировщик тоже подошёл к задаче без халтуры. Проверил, что письмо приходит на зарегистрированный адрес. Убедился, что по неизвестному email система не раскрывает наличие аккаунта. Попробовал просроченную ссылку, невалидный пароль и повторный переход по уже использованной ссылке.

Старый пароль после сброса не работал. Новый работал. Все тесты прошли, задачу выпустили.

Казалось бы, что здесь ещё можно сломать?

Через 10 минут пароль снова не подошел

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

Новый пароль больше не подходил.

В журнале безопасности нашли две смены пароля. Первую выполнил сам пользователь. Вторую выполнил кто‑то с другого IP‑адреса спустя восемь минут.

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

Перед восстановлением пользователь дважды нажал кнопку «Восстановить пароль». Первое письмо задержалось, поэтому через несколько минут он запросил второе. В итоге на почту пришли две ссылки.

Пользователь открыл более свежее письмо, задал новый пароль и вошёл в аккаунт.

А первая ссылка так и осталась лежать в почте.

Что здесь не проверили?

Попробуйте пока не читать дальше и восстановить последовательность самостоятельно.

Тестировщик ведь проверил одноразовость ссылки. После смены пароля открыть её повторно было нельзя. Срок действия тоже работал. Старый пароль система больше не принимала.

Тогда откуда взялась вторая смена?

Полезно задать себе три вопроса.

  1. Что происходит со старой ссылкой после выпуска новой?

  2. Что происходит со всеми остальными ссылками после успешной смены пароля?

  3. Одноразовая ссылка является свойством конкретного токена или всей операции восстановления?

Ответ

Скрытый текст

Проверили повторное использование одной и той же ссылки, но не проверили несколько активных ссылок для одного аккаунта.

Получилась такая цепочка.

  1. Пользователь запросил восстановление и получил ссылку A.

  2. Затем отправил ещё один запрос и получил ссылку B.

  3. По ссылке B установил новый пароль.

  4. Ссылка B стала недействительной.

  5. Ссылка A продолжила работать.

  6. По ней пароль можно было изменить ещё раз.

Формально каждая ссылка оставалась одноразовой. Ссылку B использовали один раз, ссылку A тоже использовали один раз. Отдельные проверки проходили идеально.

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

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

Что об этом говорит OWASP

OWASP рассматривает восстановление пароля не как вспомогательную форму, а как альтернативный механизм аутентификации. Фактически пользователь получает доступ к аккаунту без предъявления действующего пароля. Поэтому такой процесс не должен быть слабее обычного входа, иначе атакующий просто обойдёт основную защиту через recovery flow.

Токен из письма в этой модели становится временным подтверждением личности. OWASP рекомендует генерировать такие токены криптографически стойким способом, обеспечивать достаточную энтропию, безопасно хранить их на стороне сервера, связывать с конкретным пользователем, ограничивать срок действия и запрещать повторное использование. Для ссылок восстановления также нужно использовать HTTPS, защищаться от перебора и не допускать утечки токена через заголовок Referer, сторонние скрипты или некорректно сформированный домен.

Отдельное внимание OWASP уделяет сопутствующим рискам. Ответ системы и время его формирования не должны позволять определить, зарегистрирован ли указанный email. Количество запросов на восстановление необходимо ограничивать, иначе механизм можно использовать для автоматизированных атак или массовой отправки писем. После успешной смены пароля пользователя следует уведомить, а существующие сессии нужно либо инвалидировать автоматически, либо предложить пользователю завершить их.

При этом OWASP не устанавливает единственное обязательное правило для нескольких одновременно выпущенных ссылок. Например, стандарт напрямую не требует аннулировать первую ссылку сразу после создания второй. Такое поведение зависит от модели угроз и требований продукта.

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

На практике это обычно означает, что успешная смена пароля инвалидирует все незавершённые запросы восстановления для этого аккаунта. Более строгая модель может отзывать предыдущие токены уже при создании нового. Это не дословное требование OWASP, а архитектурный вывод из его принципов одноразовости токена, ограничения срока действия и недопустимости сохранения альтернативного пути к компрометации аккаунта.


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

На открытых уроках OTUS можно потренироваться находить такие разрывы на практике:

Участие бесплатное, но требуется регистрация.

Больше бесплатных уроков июля смотрите в дайджесте.

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


  1. usrsse2
    15.07.2026 10:41

    Более строгая модель может отзывать предыдущие токены уже при создании нового.

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


  1. Pochemuk
    15.07.2026 10:41

    Например, новый запрос инвалидирует все предыдущие ссылки.

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

    Еще круче будет, если раз в пару месяцев система будет требовать сменить пароль. Вместо кнопки "Вход" - кнопка "Сменить пароль". На мыло падает ссылка для смены, но ... она уже не действительна.