Веб
Защита входа: brute-force, rate limit, OTP и безопасность сессии
Страница входа — это дверь в систему. Практические проверки: ограничение подбора паролей, OTP-коды, сброс пароля и правильное управление сессией.
· 8 мин. чтения

Страница входа — самая проверяемая часть любого портала или кабинета. И экспертиза, и реальный атакующий смотрят сюда в первую очередь: можно ли подобрать пароль, узнать, существует ли пользователь, обойти OTP-код, закрывается ли сессия после выхода. Ниже разберём сценарий входа по шагам.
1. Brute-force и rate limit
Форма входа без ограничений открыта для автоматического подбора пароля. Защита должна быть многослойной:
- Ограничение по аккаунту + IP. Ограничения только по IP мало — атакующий меняет адреса. Ограничение только по аккаунту приводит к блокировке настоящего пользователя. Совмещайте оба.
- Общее ограничение. Ограничивайте и попытки с одного IP по многим аккаунтам (password spraying).
- Прогрессивная задержка или CAPTCHA. После нескольких неудачных попыток — CAPTCHA или увеличение времени ожидания.
Пример на Laravel:
RateLimiter::for('login', function (Request $request) {
$login = Str::lower((string) $request->input('phone'));
return [
Limit::perMinute(5)->by($login.'|'.$request->ip()),
Limit::perMinute(30)->by($request->ip()),
];
});
Проверить просто: отправьте неверный пароль 10–15 раз подряд и посмотрите, переходит ли ответ в 429. Отдельно проверьте маршрут входа через API (для мобильного приложения) — о нём часто забывают.
2. Пользователя нельзя вычислить
Разные сообщения «Пользователь не найден» и «Неверный пароль» подсказывают атакующему, какой логин существует (user enumeration). Используйте одно общее сообщение: «Неверный логин или пароль». На страницах сброса пароля и регистрации — тот же принцип: «Если такой аккаунт существует, инструкция отправлена». Большая разница во времени ответа тоже даёт информацию, поэтому лучше выполнять проверку хеша пароля и для несуществующего пользователя.
3. Хранение паролей
Пароли хранятся только с помощью адаптивных хеш-функций: bcrypt или Argon2id. MD5, SHA1 или «собственное шифрование» — нет. В Laravel Hash::make() делает это сам; если есть пароли, перенесённые из старой системы, проверьте, что они перехешируются при следующем входе. Минимальная длина и проверка по списку известных утёкших паролей соответствуют рекомендациям OWASP ASVS.
4. OTP-коды
Одноразовый код по SMS или в приложении даёт дополнительную защиту, но и он должен быть построен правильно:
- Короткий срок действия — несколько минут.
- Ограниченное число попыток. Шестизначный код без ограничений можно подобрать; после 3–5 ошибок код аннулируется.
- Одноразовость. Использованный код повторно не принимается.
- Ограничение повторной отправки. Иначе форма превращается в сервис рассылки SMS за ваш счёт (SMS bombing).
- Кода нет в ответе и логах. Добавить код в ответ API на время тестов и забыть убрать — частая ошибка.
- Код привязан к аккаунту. Кодом, отправленным одному пользователю, нельзя войти в другой аккаунт.
- Проверка на сервере. Не оставляйте решение «код верный» на стороне мобильного клиента.
OTP — дополнительный замок. Если его можно подбирать бесконечно, это не замок.
5. Сброс пароля
Сценарий сброса пароля часто слабее самого входа. В экспертизе нашего собственного продукта внедрение через заголовок Host было опасно именно здесь: поскольку приложение доверяло X-Forwarded-Host, ссылка сброса могла формироваться с доменом атакующего. Исправление — доверять только заголовкам X-Forwarded-For/Proto/Port и принудительно задать базовый URL в production.
Дополнительные проверки:
- токен сброса случайный, длинный и одноразовый;
- срок действия ограничен;
- после смены пароля все остальные сессии и токены аннулируются;
- запросы на сброс тоже под rate limit.
6. Безопасность сессии
После успешного входа безопасность переходит к сессии:
- ID сессии обновляется после входа (против session fixation). В Laravel —
$request->session()->regenerate(). - При выходе сессия полностью аннулируется, а не только удаляется cookie:
invalidate()иregenerateToken(). - Флаги cookie:
Secure,HttpOnly,SameSite=LaxилиStrict. - Тайм-аут неактивности. Для финансовых и государственных систем уместен короткий idle timeout.
- Список устройств и возможность «выйти на других устройствах» —
Auth::logoutOtherDevices().
В мобильных приложениях токен сессии должен храниться не в обычных SharedPreferences или UserDefaults, а в Keystore/Keychain. В нашей мобильной экспертизе незашифрованное хранение было находкой высокого уровня и закрыто через flutter_secure_storage. При выходе также очищаются данные WebView.
7. Дополнительный слой для администраторов
Админ-панель должна быть защищена сильнее, чем кабинет обычного пользователя: обязательная двухфакторная аутентификация, по возможности IP allowlist или VPN, отдельный короткий срок сессии. Перенос админки на другой путь сам по себе не защита, но вместе с allowlist уменьшает шум от автоматических сканеров.
8. Логирование и оповещения
Записывайте в лог успешные и неудачные входы, смену пароля, ошибки OTP и блокировки — но не сами пароли и коды. Настройте оповещение о необычно большом числе попыток на один аккаунт или с одного IP. Полезно также уведомлять пользователя о входе с нового устройства.
Быстрая проверка
- 15 неверных попыток — появляется 429 или CAPTCHA?
- Сообщение одинаково для существующего и несуществующего логина?
- Можно ли ввести OTP неверно 10 раз?
- Работает ли повторно использованный OTP или ссылка сброса?
- Открывается ли страница со старым cookie после выхода?
- Закрывается ли сессия на другом устройстве после смены пароля?
Вывод
Защита входа — это не одна функция, а набор небольших правил. Проверив каждое по отдельности, вы подготовите часть системы, которой экспертиза уделяет больше всего внимания.
Sipar проверяет сценарии входа, OTP и сессий до экспертизы и закрывает найденные дыры. Оставьте заявку на сканирование.


