Разработка системы восстановления пароля в мобильном приложении
Восстановление пароля — один из самых уязвимых flow с точки зрения безопасности. При этом реализуется часто небрежно: "отправили email — дальше не наша зона ответственности". На деле мобильная часть несёт ответственность за несколько критических моментов.
Уязвимости, которые чаще всего пропускают
User enumeration. Форма "Введите email" не должна сообщать, зарегистрирован ли этот адрес. Сообщение "Если email зарегистрирован, мы отправим письмо" — стандартная практика. Сообщение "Такого пользователя не существует" — раскрытие информации об аккаунтах. Некоторые проекты считают это незначительным — но в контексте GDPR это потенциально проблема.
Rate limiting на клиенте. Кнопка "Отправить повторно" должна быть заблокирована на 60–120 секунд после нажатия. Без этого пользователь (или скрипт) может spam-бомбардировать чужой email восстановительными письмами. Rate limiting нужен и на сервере, но клиент не должен облегчать атаку.
Deep link security. Ссылка восстановления вида myapp://reset-password?token=xyz — надо обработать корректно. На Android, если не настроены App Links с Digital Asset Links, любое приложение может перехватить custom scheme. Используем HTTPS Universal Links (iOS) и HTTPS App Links (Android).
Основной flow
- Пользователь вводит email → кнопка "Восстановить" (деактивирована до валидного email).
- Запрос на сервер → показываем "Проверьте почту" независимо от результата (нет user enumeration).
- В письме — ссылка с одноразовым токеном и
expires_in(обычно 15–60 минут). - Пользователь тапает ссылку → приложение открывается через Universal/App Link.
- Токен из URL → экран "Новый пароль".
- Пользователь вводит пароль дважды (или один раз с кнопкой "показать").
- PATCH/POST на сервер с токеном + новым паролем.
- Успех → автоматический вход (получаем access/refresh tokens) → главный экран.
Обработка deep link в приложении
Если приложение не установлено — ссылка должна открыть web-версию (App Clip или просто веб-форма). Это настраивается через Associated Domains + apple-app-site-association (iOS) и App Links + assetlinks.json (Android).
В SwiftUI + AppCoordinator:
// SceneDelegate или App с @main
.onOpenURL { url in
guard let token = url.queryParameters["token"] else { return }
coordinator.navigate(to: .resetPassword(token: token))
}
Токен передаём в ViewModel Reset-экрана, не держим его в URL или навигационном стеке дольше необходимого.
Экран нового пароля
SecureField с кнопкой "показать/скрыть" — стандарт. textContentType(.newPassword) на iOS предложит генератор паролей Keychain. Это полезно — лучше пусть пользователь возьмёт сгенерированный пароль, чем поставит "qwerty123".
Индикатор надёжности пароля — цветовая полоса с расчётом в реальном времени. Используем zxcvbn (есть Swift и Kotlin порты) — более честная оценка, чем "есть цифра + буква + символ".
После успешной смены — инвалидируем все активные сессии (это серверная задача, но мобильное приложение должно принять новые токены и убрать старые из Keychain).
SMS-recovery как альтернатива
OTP на телефон вместо email-ссылки. Плюс: не надо ждать письмо, не зависит от спам-фильтров. Минус: SIM swap атака — злоумышленник переоформляет номер и перехватывает OTP. Для высокочувствительных приложений SMS recovery без дополнительного фактора недостаточна.
Сроки
Full flow восстановления пароля (email-форма, deep link handling, экран нового пароля, автологин) — 4–7 рабочих дней на одну платформу. Настройка Universal/App Links + Apple App Site Association, если ещё не сделаны — плюс 1–2 дня.







