Аутентификация пользователей: что чаще всего ломают при быстром запуске
Семь системных ошибок, которые превращают рабочий продукт в открытую дверь для атакующих
Введение: почему аутентификацию ломают именно на старте
Первые спринты живут под давлением. MVP надо показать инвесторам, демо должно работать, дедлайн горит. В такой среде аутентификация воспринимается как сантехника: пусть работает, потом защитим. Этот момент «потом» наступает редко.
Результат предсказуем. По данным Verizon DBIR 2025, охватившего более 22 000 инцидентов и 12 000 подтверждённых утечек, компрометация учётных данных была ведущим вектором начального доступа второй год подряд [1]. В 54% случаев заражения ransomware в логах инфостилеров заранее присутствовали корпоративные credentials жертвы [1].
Атакующие не взламывают аутентификацию — они её обходят. Потому что разработчики в спешке делают одни и те же ошибки, превращая слой проверки личности в пустую формальность.
Эта статья разбирает семь самых частых ошибок по порядку: от хеширования паролей до сброса пароля. Для каждой — механизм уязвимости, как её ищут атакующие, и конкретный fix.
1. Слабое хеширование паролей: MD5, SHA-1 и «быстрые» алгоритмы
Механизм
Хеширование пароля должно быть намеренно медленным. Это не баг — это функция. Цель: сделать брутфорс дорогим для атакующего, даже если база данных утекла.
Самая частая ошибка при старте — использование быстрых криптографических функций. SHA-256 выполняется менее чем за микросекунду на современном CPU. Современные GPU обрабатывают несколько миллиардов SHA-256 в секунду [4]. База с SHA-256-хешами без соли вскрывается за часы для любого пароля длиной до 10 символов.
MD5 и SHA-1 уже давно считаются криптографически сломанными. Тем не менее они встречаются в новом коде — особенно когда разработчик берёт готовый snippet или AI-ассистент генерирует пример «для MVP».
Что делать
OWASP Password Storage Cheat Sheet однозначно рекомендует Argon2id как приоритетный выбор для новых приложений [5]. Argon2 выиграл Password Hashing Competition в 2015 году и специально разработан против GPU- и ASIC-атак за счёт memory-hardness. bcrypt остаётся приемлемым выбором для legacy-систем, где Argon2 недоступен, с минимальным work factor 10 [5]. Важный нюанс: bcrypt имеет лимит 72 байта входных данных в большинстве реализаций, и пароль должен быть ограничен соответственно.
Никогда не комбинируйте: bcrypt(md5(password)) не улучшает безопасность и может снижать энтропию [6].
Соль должна быть уникальной для каждого пароля, криптографически случайной, и её нельзя переиспользовать или хранить как статическое значение [6].
2. Отсутствие rate limiting на аутентификационных эндпоинтах
Механизм
Если на /login, /api/token, /forgot-password и /verify-otp нет ограничений на количество запросов в единицу времени — атакующий получает неограниченное окно для:
- Брутфорса паролей: перебор словаря rockyou.txt занимает минуты при скорости в тысячи запросов в секунду.
- Credential stuffing: промышленно масштабируемый перебор пар логин/пароль из утёкших баз. По данным Verizon DBIR 2025, в медианном случае credential stuffing составлял 19% от всех попыток аутентификации в SSO-логах [1].
- Атак на OTP: перебор 6-значного кода — 1 000 000 вариантов. Без rate limiting — не преграда.
OWASP фиксирует, что приложения, допускающие автоматизированные атаки вроде credential stuffing, квалифицируются как Authentication Failures — категория A07 в новом Top 10:2025 [2].
Характерный антипаттерн при быстром запуске: rate limiting ставят на /api в целом, но забывают применить более строгую политику именно к аутентификационным эндпоинтам. Или защищают /login, но оставляют открытым /forgot-password.
Что делать
OWASP API Security прямо указывает: механизмы защиты от брутфорса на аутентификационных эндпоинтах должны быть строже общих лимитов API [7].
Практический подход:
- Rate limiting по IP и по аккаунту раздельно — IP-блокировка обходится через VPN или ротирующие прокси, блокировка по аккаунту этого не боится.
- Прогрессивная задержка после серии неудач: exponential backoff.
- Adaptive CAPTCHA после 3–5 неудачных попыток, а не постоянная.
- Счётчик попыток не сбрасывается при успешном входе — иначе атакующий вставляет свои credentials в середину словарного списка [8].
- Логирование серий неудач и алертинг в реальном времени.
3. Уязвимая реализация JWT
Механизм
JWT (JSON Web Token) — наиболее распространённый способ передачи аутентификационного контекста в современных API. Именно поэтому он так часто реализуется неверно.
Список системных ошибок при быстром запуске [9][10]:
Алгоритм none. Ранние JWT-библиотеки принимали "alg":"none" как валидный вариант, означающий отсутствие подписи вообще. Атакующий убирает подпись, меняет payload (например, userId или role) и сервер принимает токен как легитимный.
Algorithm confusion. Сервер поддерживает и HS256 (симметричный), и RS256 (асимметричный). Атакующий переключает токен на HS256 и в качестве секрета использует публичный ключ сервера — который доступен публично. Уязвимость CVE-2023-29357 в Microsoft SharePoint эксплуатировала именно эту логику [11].
Отсутствие верификации подписи. Разработчик использует decode() вместо verify() — декодирует содержимое, но не проверяет подпись. Это случается при отладке и остаётся в продакшене [9].
Слабый секрет. JWT с алгоритмом HS256 подписывается произвольной строкой. Секрет "1234" или "secret" вскрывается брутфорсом за минуты. Разработчики копируют пример из документации и забывают заменить placeholder [10].
Хардкод секрета в репозитории. Удалён из HEAD, но история коммитов доступна атакующему.
Отсутствие проверки exp (expiration). Просроченный токен остаётся валидным бесконечно.
Игнорирование aud и iss. Токен, выпущенный для одного сервиса, принимается другим — cross-service token reuse.
Что делать
- Явно указывать разрешённые алгоритмы, никогда не принимать
none. - Использовать
verify(), а неdecode()— всегда, без исключений. - Для HS256: секрет длиной минимум 256 бит, сгенерированный CSPRNG, не хранящийся в коде.
- Для продакшена рекомендовать RS256/ES256 (асимметричные схемы).
- Устанавливать короткий
exp(15–60 минут для access tokens) и валидировать его. - Валидировать
audиissв каждом сервисе. - Rotation секретов: при смене ключа инвалидировать старые токены.
Отдельная ловушка, актуальная сейчас: LLM-ассистенты генерируют JWT-код с паттернами из исторических репозиториев, которые предшествуют современным практикам безопасности [3]. Каждая строка сгенерированного кода аутентификации требует того же SAST-ревью, что и написанная вручную.
4. Ошибки OAuth 2.0 и OIDC
Механизм
OAuth и OIDC упрощают интеграцию авторизации, но сложность протоколов создаёт большую поверхность для ошибок. В 2025 году Missing Authentication стал уязвимостью №1 по данным APISecurity.io, составив 17% всех API-инцидентов [12].
Наиболее частые ошибки [13][14]:
Некорректная валидация redirect_uri. Самая распространённая и опасная ошибка. Сервер авторизации принимает wildcard или частичные совпадения URL. Атакующий подставляет собственный домен в параметр redirect_uri — и authorization code утекает к нему. RFC 9700 (март 2025) требует точного посимвольного совпадения URI для всех клиентов [13].
Отсутствие PKCE. Proof Key for Code Exchange защищает authorization code flow от перехвата — особенно критично для публичных клиентов (SPA, мобильные приложения). RFC 9700 теперь требует PKCE для всех типов клиентов [13]. В 2024 году отсутствие PKCE присутствовало в более чем 35% атак на OAuth [14].
Implicit flow. Устаревший поток, при котором access token передаётся прямо в URL-фрагменте. Токен попадает в browser history, referer-заголовки, логи. Несмотря на официальное устаревание, встречается в новом коде.
Отсутствие state-параметра. Без криптографически случайного state невозможно защититься от CSRF на этапе OAuth callback.
Скомпрометированные OAuth-токены интеграций. Инцидент Salesloft-Drift в 2025 году: атакующие похитили OAuth-токены интеграции и получили доступ к данным более чем 700 компаний через Salesforce. MFA не сработал, поскольку момент аутентификации уже произошёл при первоначальной авторизации интеграции [15].
Что делать
- Только Authorization Code Flow + PKCE. Deprecated Implicit Flow — убрать.
- Строгий allowlist redirect_uri: только точные совпадения, никаких wildcard.
- Криптографически случайный
stateв каждом запросе, валидируется на callback. - Короткое время жизни access token (минуты), refresh token rotation.
- Регулярный аудит разрешений интеграций: какие OAuth-приложения имеют доступ и с какими скоупами.
5. Хардкод секретов в репозитории
Механизм
Самая острая ловушка при быстром запуске: .env-файл с паролями к базе данных, JWT-секрет в коде, AWS-ключ в конфигурационном файле — всё это коммитится в репозиторий «временно», и остаётся там навсегда.
Ключевое свойство git: история неизменна. Секрет, удалённый из HEAD-ветки восемь месяцев назад, живёт в истории коммитов — и доступен атакующему. AWS-ключ в истории приватного репозитория может оставаться активным и предоставлять доступ к продакшн-данным [16].
По данным DevSecOps-аудитов: секреты, попавшие в публичный репозиторий, как правило используются атакующими менее чем через 15 минут [17]. Это автоматизированные боты, постоянно сканирующие GitHub/GitLab на характерные паттерны.
Помимо репозитория, секреты утекают через:
- Артефакты CI/CD — логи, кэш, выгрузки из пайплайна.
- Форки репозитория: форк сохраняет полную историю, включая удалённые секреты.
- OIDC-токены GitHub Actions: CVE-2024-5037 показала, что утёкший OIDC-токен из лога давал атакующему доступ к credentials CI/CD-аккаунта [16].
Что делать
- Никогда не коммитить секреты в репозиторий. Использовать
.gitignoreдля.envи конфигурационных файлов с credentials. - Pre-commit hooks с gitleaks или аналогичным инструментом: секрет не попадает в коммит.
- Secret scanning в CI: GitHub Advanced Security, GitLab Secret Detection, trufflehog.
- Если секрет уже попал в историю — не просто удалить из HEAD, а провести полное rewrite истории и ротацию скомпрометированных credentials.
- Хранить секреты в специализированных решениях: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault. Для небольших команд — корпоративный менеджер паролей с API [61].
- В CI/CD использовать OIDC-аутентификацию вместо долгоживущих ключей там, где платформа поддерживает.
6. Небезопасное управление сессиями
Механизм
Аутентификация завершается — начинается сессия. Именно на этом уровне MFA теряет смысл: атакующий не ломает второй фактор, он крадёт сессионный токен после того, как пользователь уже прошёл все проверки.
Техника AiTM (Adversary-in-the-Middle) с инструментами Evilginx2, Tycoon 2FA и EvilProxy превратилась в промышленную: обратный прокси перехватывает сессионный cookie в реальном времени, пока пользователь проходит MFA. Единственный устойчивый к этому метод — FIDO2/WebAuthn, привязанный к легитимному домену криптографически [15].
Распространённые ошибки в реализации [18][19]:
Отсутствие HttpOnly на сессионном cookie. JavaScript получает доступ к cookie — любой XSS позволяет похитить сессию.
Отсутствие Secure. Cookie передаётся по HTTP, а не только по HTTPS — перехватывается в незащищённых сетях.
Отсутствие SameSite=Strict/Lax. Cookie отправляется с cross-site запросами — открывает путь к CSRF.
Хранение токенов в localStorage. Дерево XSS-атак становится полным: localStorage.getItem('token') в одной строке JavaScript.
Session fixation. Идентификатор сессии не меняется после успешного логина. Атакующий навязывает жертве свой session ID до аутентификации — и получает доступ после неё.
Отсутствие инвалидации сессий при логауте. Токен «живёт» после выхода — украденный cookie продолжает работать.
Долгий TTL без переаутентификации. Сессия на 30 дней без проверки контекста — пространство для злоупотреблений.
Что делать
- Cookie флаги:
HttpOnly,Secure,SameSite=Strict— для всех сессионных cookies. - Токены не хранить в localStorage. Для SPA — httpOnly cookies + CSRF-токены.
- Генерировать новый session ID после каждого успешного логина.
- Немедленная инвалидация токенов на сервере при логауте — не только на клиенте.
- Короткий TTL access tokens (15–60 минут), refresh token rotation.
- Для чувствительных операций (смена email, пароля, 2FA-настройки) — обязательная переаутентификация.
- Для критичного периметра — рассмотреть FIDO2/WebAuthn как единственный MFA-метод, устойчивый к AiTM.
7. Небезопасный сброс пароля
Механизм
Функция «Забыли пароль?» является точкой входа с логикой аутентификации, к которой часто применяют меньше строгости, чем к основному логину. Это ошибка.
OWASP Forgot Password Cheat Sheet прямо указывает: эндпоинты восстановления пароля должны иметь те же механизмы защиты от брутфорса, что и логин [20].
Типичные уязвимости при быстром запуске:
Предсказуемые токены. Токен сброса сгенерирован на основе timestamp или userId — вскрывается тривиально. OWASP рекомендует минимум 128 бит энтропии [20].
Отсутствие TTL у токена. Токен действует неограниченно — перехваченная ссылка из письма работает через неделю.
Многократное использование. Токен не инвалидируется после использования — replay attack.
Отсутствие rate limiting. Атакующий запрашивает сброс в цикле, заполняя inbox жертвы, затрудняя обнаружение легитимной ссылки.
User enumeration. Разные ответы для существующих и несуществующих аккаунтов («такого email нет в системе») раскрывают базу пользователей. OWASP требует возвращать одинаковый ответ и одинаковое время для обоих случаев [20].
Привязка токена не к аккаунту. Токен, выпущенный для одного аккаунта, принимается для другого. Атакующий запрашивает сброс для своего аккаунта, перехватывает токен и использует его против чужого [21].
Host Header Injection при генерации ссылки. URL сброса строится на основе заголовка Host входящего запроса. Атакующий подставляет свой домен — и ссылка ведёт к нему [20].
Что делать
- Токены: криптографически случайные (CSPRNG), минимум 128 бит энтропии, URL-safe base64.
- TTL: 15–60 минут максимум.
- Single-use: инвалидация сразу после использования.
- Привязка к конкретному userId на уровне базы данных.
- URL генерировать только на основе жёстко заданного домена, не из
Host-заголовка. - Единый ответ и единое время для существующих и несуществующих аккаунтов.
- Rate limiting, эквивалентный или строже, чем на
/login. - Уведомлять пользователя о факте запроса на сброс (даже если аккаунт не найден — без раскрытия этого факта).
Практический чек-лист: аутентификация до merge
Хеширование паролей
- [ ] Используется Argon2id или bcrypt (work factor ≥ 10)
- [ ] Нет MD5, SHA-1, SHA-256 для паролей
- [ ] Уникальная криптографически случайная соль для каждого пароля
- [ ] Нет
bcrypt(md5(password))
Rate limiting
- [ ] Rate limiting на
/login,/token,/forgot-password,/verify-otpстроже, чем на остальных эндпоинтах - [ ] Ограничение по IP и по аккаунту раздельно
- [ ] Счётчик не сбрасывается при успешном входе
- [ ] Прогрессивная задержка или CAPTCHA после N неудач
JWT
- [ ] Алгоритм
noneотклоняется явно - [ ] Используется
verify(), а неdecode() - [ ] Секрет ≥ 256 бит, CSPRNG, не в коде
- [ ]
expустановлен и валидируется - [ ]
audиissвалидируются в каждом сервисе
OAuth/OIDC
- [ ] Только Authorization Code Flow + PKCE
- [ ] Строгий allowlist redirect_uri, без wildcard
- [ ] State-параметр: криптографически случайный, валидируется на callback
- [ ] Implicit flow удалён
Секреты
- [ ] Нет credentials в коде и конфигах, попадающих в git
- [ ]
.envв.gitignore - [ ] Pre-commit hook или secret scanning в CI
- [ ] История коммитов проверена на предмет утёкших секретов
Управление сессиями
- [ ] Cookie:
HttpOnly,Secure,SameSite=Strict - [ ] Токены не в
localStorage - [ ] Session ID регенерируется после логина
- [ ] Сессия инвалидируется на сервере при логауте
- [ ] TTL access token ≤ 60 минут
Сброс пароля
- [ ] Токен: ≥ 128 бит энтропии, CSPRNG
- [ ] TTL ≤ 60 минут
- [ ] Single-use, привязан к конкретному userId
- [ ] URL не строится из
Host-заголовка - [ ] Единый ответ для существующих и несуществующих аккаунтов
- [ ] Rate limiting эквивалентен
/login
Ошибки, которые часто пропускают при ревью
Отдельного внимания заслуживают паттерны, которые не бросаются в глаза при быстром просмотре PR.
Изменение email без переаутентификации. Атакующий, получив сессионный токен, меняет email жертвы и запускает сброс пароля — полный захват аккаунта без знания пароля. OWASP API Security специально выделяет этот сценарий [7].
Отсутствие invalidation SSO-токенов. При логауте из SSO токены других сессий остаются живыми. Пользователь нажал «выйти» — а сессия на другом устройстве работает.
Ghost logins. SSO настроен, но приложение не требует SSO-only: локальные credentials, созданные до внедрения SSO, продолжают работать [15].
Scoped access tokens без проверки scope. Токен с ограниченными правами принимается эндпоинтом, который должен требовать более широких прав — но проверки нет.
Конфигурация CORS без аутентификационного контекста. Слишком широкий CORS позволяет cross-origin запросам нести аутентификационные заголовки или cookies.
Почему AI-генерируемый код воспроизводит эти ошибки
Согласно анализу OWASP Top 10:2025, LLM-ассистенты систематически генерируют код с паттернами A03 (Injection), A07 (Authentication Failures) и A02 (Cryptographic Failures) — потому что обучены на историческом коде из репозиториев, предшествующих современным стандартам безопасности [3].
Это не значит, что AI-кодинг нужно запретить. Это значит, что каждый сгенерированный PR с аутентификационной логикой требует того же уровня проверки, что и написанный вручную. Проблема не в скорости генерации — проблема в том, что при быстром запуске дифы читают невнимательно. Рабочий PR не обязательно безопасен.
Здесь и появляется разрыв, который сложно закрыть только через code review: когда JWT-секрет выглядит правильно, но взят из примера в документации; когда rate limiting есть на /api, но не на /auth; когда bcrypt использован, но work factor — дефолтный минимум. Такие вещи нужно проверять автоматически на уровне каждого изменения — до merge, а не в пост-мортеме. Именно эту задачу решает HighVibe: анализирует конкретные изменения в коде и конфигурации, объясняет проблему понятным языком и предлагает следующий шаг.
Тренды: что изменится в ближайшее время
Passkeys и FIDO2 как ответ на AiTM. Единственный метод аутентификации, устойчивый к атакам типа Adversary-in-the-Middle, — это WebAuthn/FIDO2, криптографически привязанный к домену. Пока большинство стартапов реализует TOTP или SMS, что по-прежнему уязвимо для Evilginx и аналогов.
RFC 9700 и обязательный PKCE. Опубликованный в марте 2025 года OAuth 2.0 Security Best Current Practice делает PKCE обязательным для всех типов клиентов — не только публичных. Это меняет минимальный стандарт для любого нового OAuth-флоу.
Passkeys в мейнстриме. Крупные платформы активно продвигают passkeys как замену паролям. Для стартапов это означает, что библиотеки уже достаточно зрелые для внедрения — и пользователи начинают ожидать этой опции.
Регуляторное давление. Рост числа утечек и ужесточение требований регуляторов (включая поправки к 152-ФЗ с оборотными штрафами) делают security-долг аутентификации всё более дорогостоящим.
Заключение: что делать прямо сейчас
Аутентификация — не модуль, который «потом защитим». Это граница, за которой начинаются пользовательские данные. Пробой на этой границе при быстром запуске стоит дороже всего: репутация, данные клиентов, регуляторные риски.
Хорошая новость: все семь описанных ошибок устраняются на уровне конкретного изменения кода. Нет ничего, что требовало бы переписывать систему целиком.
Практический следующий шаг:
- Пройдите по чек-листу выше для текущего состояния продукта — особенно для аутентификационных эндпоинтов.
- Добавьте secret scanning в pre-commit hooks и CI.
- Убедитесь, что
HttpOnly,Secure,SameSiteвыставлены на сессионных cookies. - Замените SHA-*/MD5 на Argon2id или bcrypt в хеширование паролей.
- Добавьте явный allowlist алгоритмов в JWT-верификацию.
Каждый из этих шагов — одно изменение. Одно изменение до merge, а не постмортем после инцидента.
Источники
[1] Verizon — "2025 Data Breach Investigations Report" — https://www.verizon.com/business/resources/articles/credential-stuffing-attacks-2025-dbir-research/
[2] OWASP — "A07 Authentication Failures — OWASP Top 10:2025" — https://owasp.org/Top10/2025/A07_2025-Authentication_Failures/
[3] SecComply — "OWASP Top 10–2025 Edition: What Every Developer and Security Team Must Know" — https://seccomply.net/resources/blog/owasp-top-10-2025
[4] Toolsana — "Password Hashing in 2026: bcrypt vs Argon2 vs scrypt vs PBKDF2 — A Practitioner's Guide" — https://toolsana.com/blog/password-hashing-2026-bcrypt-argon2-scrypt-pbkdf2-guide/
[5] OWASP — "Password Storage Cheat Sheet" — https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
[6] Deepak Gupta — "The Complete Guide to Password Hashing: Argon2 vs bcrypt vs scrypt vs PBKDF2" — https://guptadeepak.com/the-complete-guide-to-password-hashing-argon2-vs-bcrypt-vs-scrypt-vs-pbkdf2-2026/
[7] OWASP — "API2:2023 Broken Authentication — OWASP API Security Top 10" — https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/
[8] Ahmad Sopyan / Medium — "Authentication part-6: Broken brute-force protection, IP block" — https://medium.com/@AhmadSopyan/authentication-part-6-broken-brute-force-protection-ip-block-ecbb6f63779f
[9] PentesterLab — "The Ultimate Guide to JWT Vulnerabilities and Attacks" — https://pentesterlab.com/blog/jwt-vulnerabilities-attacks-guide
[10] Flipper File — "Common JWT Vulnerabilities: Mistakes That Lead to Token Abuse" — https://flipperfile.com/developer-guides/security/common-jwt-vulnerabilities/
[11] NDSS Symposium — "Token Time Bomb: Evaluating JWT Implementations for Vulnerability Discovery" — https://www.ndss-symposium.org/wp-content/uploads/2026-f697-paper.pdf
[12] APISecurity.io — "Issue 286: The APISecurity.io Top 5 API Vulnerabilities in 2025" — https://apisecurity.io/issue-286-the-apisecurity-io-top-5-api-vulnerabilities-in-2025/
[13] Security Boulevard — "redirect_uri_mismatch in OAuth 2.0 and OIDC: 7 Causes and How to Fix Each" — https://securityboulevard.com/2026/05/redirect_uri_mismatch-in-oauth-2-0-and-oidc-7-causes-and-how-to-fix-each/
[14] MoldStud — "Common OAuth Security Vulnerabilities and How to Avoid Them" — https://moldstud.com/articles/p-common-oauth-security-vulnerabilities-and-how-to-avoid-them-a-comprehensive-guide
[15] Obsidian Security — "MFA Bypass Explained: Why Traditional Auth Fails & Prevention Tips" — https://www.obsidiansecurity.com/blog/mfa-bypass-explained-why-traditional-auth-fails-prevention-tips
[16] Codeby.net — "Secrets scanning: находим утёкшие токены и API-ключи" — https://codeby.net/threads/secrets-scanning-utechka-tokenov-i-api-klyuchei-nakhodim-utekshiye-credentials-v-github-gitlab-bitbucket-i-jira.93779/
[17] Codeby School — "Внедрение DevSecOps: Автоматизация безопасности в CI/CD" — https://codeby.school/blog/informacionnaya-bezopasnost/devsecops-bez-boli-gotovyy-gitlab-ci-pipeline-za-15-minut
[18] Brandefense — "MFA Doesn't Protect You — Cookies Give You Away: The Rise Of Session Hijacking" — https://brandefense.io/blog/mfa-doesnt-protect-you-cookies-give-you-away-the-rise-of-session-hijacking/
[19] Clerk — "How to Secure Web App Auth: A Comprehensive Guide" — https://clerk.com/articles/authentication-security-in-web-applications
[20] OWASP — "Forgot Password Cheat Sheet" — https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html
[21] Undercode Testing — "The Account Takeover Bomb: Deconstructing A Critical Password Reset Vulnerability" — https://undercodetesting.com/the-account-takeover-bomb-deconstructing-a-critical-password-reset-vulnerability/