Один запрос под микроскопом: как сервис решает, бот перед ним или человек
Что успевает произойти между «пришёл запрос» и «отдали страницу»: какие признаки смотрим, чему не верим и почему сомнение всегда в пользу посетителя.
6 мин чтения
Обычно про защиту от ботов рассказывают в общих словах: «умные алгоритмы», «машинное обучение», «многоуровневый анализ». Мы решили показать конкретику и провести один запрос через движок BotBan от первой строки до вердикта. Никаких секретных формул тут не будет: секрет хорошей защиты не в формулах, а в том, чего она себе не позволяет.
Возьмём выдуманный, но типичный запрос: GET /catalog/nasosy с адреса
из хостингового диапазона, User-Agent заявляет свежий Chrome, заголовка
Accept-Language нет.
Шаг 0. Сначала те, кого проверять не надо
Прежде чем что-то считать, движок смотрит, не относится ли запрос к тем, кого трогать нельзя в принципе.
Адрес в белом списке сайта, путь вроде /payment/callback, который
владелец вынес из-под проверки, вебхук платёжной системы, мониторинг
доступности, краулер мессенджера, который тянет картинку для превью
ссылки, всё это проходит сразу и не участвует в дальнейшем разборе.
Отдельная история с поисковыми роботами. Если User-Agent говорит «я Яндекс», это не значит ничего: написать так может кто угодно. Мы делаем обратный DNS по адресу, проверяем, что имя лежит в зоне поисковика, и делаем прямой DNS по этому имени, чтобы он вернул тот же адрес. Результат запоминается на месяц. Подтверждённый робот проходит всегда, что бы ни настроил владелец сайта. А вот «робот», который проверку не прошёл, получает тяжёлый признак против себя: подделка поисковика это уже не подозрение, а улика.
Наш запрос ни под одно исключение не попал. Идём дальше.
Шаг 1. Дешёвые признаки, которые нельзя подделать
Есть вещи, которые видим мы сами, а не сообщает посетитель. Сеть, из
которой пришёл запрос: домашний провайдер, мобильный оператор,
датацентр. Репутация адреса и подсети по нашей общей базе. Что этот
адрес делал на сайте последние минуты: сколько запросов, ходил ли по
/wp-login.php и /.env, сыпал ли ошибками 404.
У нашего запроса адрес из хостинга. Это не приговор: из датацентров ходят корпоративные прокси, VPN и вполне живые люди. Но это вес в копилку.
Шаг 2. Заголовки: как выглядит настоящий браузер
Настоящий Chrome присылает не только User-Agent, а целый набор:
Accept-Language, Accept-Encoding, Sec-Fetch-*, клиентские
подсказки Sec-CH-UA. Причём в определённом составе и порядке. Скрипт,
который скопировал одну строку User-Agent, остальное обычно забывает.
Наш запрос назвался Chrome, но языка у него нет. Ещё вес.
Библиотечные подписи вроде python-requests, curl, Go-http-client
и пустой User-Agent разбираются здесь же и весят много: маскироваться
такой клиент даже не пытается.
Шаг 3. Чему мы не верим
Если сайт подключён ещё и скриптом, к серверным признакам добавляются
браузерные: navigator.webdriver, отпечаток canvas, движения мыши,
скролл, размеры экрана.
Тут важная оговорка. Всё это присылает браузер посетителя, то есть
потенциально сам бот. Поэтому уличающие признаки считаются весомыми, а
оправдывающие ограничены потолком: они могут помочь в спорном случае,
но не могут перевесить улику, которую мы увидели своими глазами. Бот,
который старательно двигает мышкой, но ходит с python-requests,
оправдания не получит.
По той же причине ни один признак в одиночку не может дотянуть до блокировки. У каждого есть предельный вес, и чтобы уйти в «блок», нужно совпадение нескольких независимых доводов.
Шаг 4. Балл и три двери
Признаки складываются в балл от 0 до 100. Дальше три двери: до 40 пропускаем, от 40 до 70 просим доказать, что перед нами браузер, от 70 блокируем. Пороги владелец сайта может двигать, но кабинет предупредит, если порог блокировки опустить слишком низко.
Наш запрос набрал что-то около 50: хостинг плюс неполные заголовки. Это спорная зона. Блокировать за такое нельзя, слишком много живых людей выглядят похоже. Пропускать молча тоже жалко.
Шаг 5. Доказательство работы вместо капчи
В спорной зоне посетитель получает не капчу, а задачу для браузера: подобрать число, при котором хеш начинается с шестнадцати нулевых бит. Это порядка 65 тысяч попыток, четверть секунды на обычном телефоне. Человек этого не замечает. Тот, кто обходит миллион страниц, платит за каждую.
Капча решает ту же задачу, но берёт плату с человека, причём тем большую, чем хуже он видит и чем меньше у него терпения. Мы считаем, что за подозрения сервиса должен платить не покупатель.
Задача одноразовая и привязана к адресу и браузеру, решение соседа не подойдёт. Решивший получает пропуск на час и дальше ходит по сайту без проверок.
Если наш запрос пришёл от скрипта без JavaScript, задачу он не решит и страницу не получит. Если это человек за странным прокси, он через четверть секунды окажется в каталоге и ничего не заметит.
Правила, которые важнее балла
Всё описанное выше можно назвать умным. Но защиту делает надёжной не ум, а ограничения, которые движок не может нарушить, даже если его очень попросить.
Новый сайт первую неделю никого не блокирует. Он считает и показывает, кого заблокировал бы. Владелец смотрит на список и только потом, осознанно, включает блокировку. Мы видели достаточно сайтов, где «ботами» оказались собственный обмен с 1С и сотрудники из офиса.
Мобильные и NAT-адреса не блокируются жёстко никогда. За одним адресом оператора сидят тысячи людей. Максимум для такого адреса проверка браузером, и это ограничение движка, а не настройка.
Чёрный список не может заблокировать поискового робота и не принимает слишком широкие сети: правило «забанить всех» физически не пройдёт валидацию.
Если блокировок стало подозрительно много, больше пятой части трафика за пять минут, сайт сам возвращается в тихий режим и владелец получает уведомление. Это страховка от собственной ошибки в настройках.
Любой сбой означает «пропустить». Недоступен наш сервер, истёк
таймаут, упал кэш, всё это заканчивается тем, что посетитель видит
сайт. Модуль на стороне клиента не может уронить страницу: никакого
вывода до вердикта, всё в try/catch. А если нужно выключить защиту
без нас, достаточно положить файл botban.off рядом с модулем.
Почему мы так устроены
У любой защиты от ботов две ошибки: пропустить бота и остановить человека. Они не равны. Пропущенный бот стоит немного серверного времени и строчки в статистике. Остановленный человек это несделанный заказ и рассказ друзьям о сайте, который «не открывается».
Поэтому движок построен несимметрично. Чтобы заблокировать, нужно несколько независимых улик. Чтобы пропустить, достаточно сомнения. По каждому решению в журнале видно, какие признаки сработали и с каким весом, и одна кнопка «это был не бот» отправляет адрес в белый список и в обучающую выборку.
Ответ на вопрос из заголовка получается такой: мы отличаем бота от человека по совпадению нескольких признаков, которые нельзя подделать одновременно, а во всех остальных случаях честно признаём, что не уверены, и не мешаем.
Читайте также
- Как посчитать долю ботов на своём сайте Средние цифры по рынку бесполезны: доля ботов зависит от тематики и от того, кому вы интересны. Как получить свою цифру, а не среднюю по рынку.
- Сайт под атакой ботов: что делать прямо сейчас Короткая инструкция для ситуации, когда разбираться некогда: сайт тормозит или лёг, и подозрение на ботов.
- Почему блокировка по странам почти не помогает Самое соблазнительное правило в настройках защиты и одно из самых вредных. Объясняем на цифрах и примерах.