Перейти к содержанию
Innovialabs

Credential stuffing и захват аккаунтов: как обнаружить и защититься

Продвинутое4 мин

Коротко

Credential stuffing — это массовая подстановка пар логин-пароль, утёкших с других сайтов; защищаются слоями: блокируют скомпрометированные пароли через k-anonymity, оценивают каждый вход по устройству и поведению, просят второй фактор только при высоком риске, фильтруют ботов и отслеживают аномалии входов.

Как работает credential stuffing

Люди повторяют пароли. Когда у одного сайта утекает база пользователей, злоумышленники берут пары email-пароль и автоматически подставляют их в другие сервисы: банки, магазины, приложения доставки, SaaS-продукты. Каждая сработавшая пара — это захват аккаунта (account takeover, ATO): украденные бонусы, сохранённые карты, персональные данные или плацдарм для мошенничества.

Почему это сложно остановить:

  • запросы идут через резидентные прокси и тысячи IP, поэтому лимиты на IP почти не срабатывают;
  • на каждый аккаунт приходится одна-две попытки, поэтому блокировки по аккаунту тоже не срабатывают;
  • боты имитируют настоящие браузеры, решают простые проверки и меняют user agent;
  • учётные данные у атакующего правильные — успешный вход выглядит полностью легитимно.

Rate limiting по-прежнему нужен, но это первый слой, а не вся защита.

Слой 1. Отклоняйте скомпрометированные пароли

Если пароль уже есть в публичных базах утечек, его обязательно попробуют. Проверяйте пароли при регистрации, смене пароля и входе — так рекомендует NIST SP 800-63B.

Сервис Pwned Passwords использует k-anonymity: вы хешируете пароль SHA-1, отправляете только первые 5 hex-символов, получаете все суффиксы с этим префиксом и сравниваете у себя. Ни пароль, ни полный хеш не покидают ваш сервер.

import hashlib
import requests

def is_breached(password: str) -> bool:
    digest = hashlib.sha1(password.encode("utf-8")).hexdigest().upper()
    prefix, suffix = digest[:5], digest[5:]
    resp = requests.get(f"https://api.pwnedpasswords.com/range/{prefix}", timeout=5)
    resp.raise_for_status()
    return any(line.split(":")[0] == suffix for line in resp.text.splitlines())

Если совпадение найдено при входе, пускайте пользователя только после дополнительной проверки и требуйте сменить пароль. Заранее решите, что делать при недоступности сервиса: обычно вход не блокируют, но событие логируют.

Слой 2. Оценивайте риск каждого входа

Собирайте сигналы и сводите их в оценку риска вместо одного правила «да/нет»:

  • Устройство: знакомы ли это устройство и браузер для аккаунта? Сохранённый токен устройства или cookie — сильный положительный сигнал.
  • Сеть: ASN дата-центра или хостинга, известный прокси или выходной узел Tor, страна, из которой пользователь никогда не входил.
  • Поведение: время между загрузкой страницы и отправкой формы, паттерны ввода и движения курсора, прямой POST в API входа без загрузки страницы.
  • История: «невозможное перемещение» между двумя входами, несколько аккаунтов с одного устройства, сброс пароля сразу после входа с нового устройства.

По возможности держите сигналы на сервере: всё, что присылает клиент, можно подделать.

Слой 3. Step-up аутентификация

Оценка риска определяет, что запросить:

РискДействие
НизкийОбычный вход
СреднийОдноразовый код или подтверждение по email
ВысокийСильный второй фактор или блокировка с уведомлением пользователя

Passkeys и коды из приложения хорошо защищают от credential stuffing: одного повторно использованного пароля уже недостаточно. SMS-коды лучше, чем ничего, но слабее против SIM swapping. Если предлагать MFA всем, а проверку запрашивать только при рискованных входах, трение остаётся низким.

Слой 4. Обнаруживайте и замедляйте ботов

  • Поставьте эндпоинт входа за bot management на edge или WAF с правилами против credential stuffing.
  • Показывайте челлендж только при подозрительных сигналах, а не на каждом входе.
  • Возвращайте одинаковую ошибку и похожее время ответа при неверном пароле и несуществующем пользователе, чтобы атакующие не могли проверять свои списки.
  • Ограничивайте сразу по нескольким измерениям: IP, подсеть, ASN, устройство, аккаунт и глобально.
  • Защитите все точки входа: мобильный API, сброс пароля, старые эндпоинты и партнёрские входы часто забывают.

Слой 5. Мониторьте аномалии входов

У credential stuffing узнаваемый рисунок в метриках. Настройте алерты на:

  • всплеск общего числа попыток входа или доли неудачных;
  • рост доли ошибок «пользователь не найден» — в списке атакующего есть email, которых у вас никогда не было;
  • попытки входа во множество аккаунтов с одного отпечатка устройства;
  • рост успешных входов с новых устройств, за которыми следует смена email, телефона или реквизитов выплат.

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

Частые ошибки

  • Ставка на один механизм, обычно CAPTCHA, решение которой боты могут отдать на аутсорс.
  • Блокировка аккаунта после нескольких неудач — так атакующие легко блокируют реальных пользователей.
  • Сообщение «этот email не зарегистрирован» на форме входа.
  • Защищённый веб-вход при открытом мобильном API.

Частые вопросы

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

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.