Как антифрод-системы маркетплейсов вычисляют IPv6-прокси по поведенческим паттернам аккаунтов
Содержание
- Что видит антифрод кроме IP-адреса
- Ротация /128 внутри /64 — главный маркер
- Геолокация по BGP-префиксу против геолокации по IP
- TLS- и HTTP-фингерпринты, которые не сходятся с IPv6
- Поведение аккаунта: тайминги, глубина просмотра, конверсия
- Пример: как выглядит сессия через IPv6-пул в логах
- Как отличить «плохой» IPv6 от «нормального»
- Код: как антифрод считает ротацию по /64
- Код: проверка префикса через BGP-данные
- Реальные кейсы из практики
- Что делать, если аккаунт уже под подозрением
IPv6-адрес сам по себе — не приговор. Проблема в том, что вокруг него строится поведенческий профиль. Антифрод маркетплейсов (Ozon, Wildberries, Amazon, eBay, AliExpress) давно перестал смотреть только на IP. Основной сигнал — как именно аккаунт ходит через этот IP.
Что видит антифрод кроме IP-адреса
Любая серьёзная система собирает с десяток слоёв телеметрии. На уровне сети — ASN, геолокация по BGP-префиксу, наличие обратной зоны (PTR), принадлежность к хостинг-провайдеру или eyeball-сети. На уровне TLS — JA3/JA4 fingerprint, набор поддерживаемых шифров, порядок расширений. На уровне HTTP — порядок заголовков, Accept-Language, наличие специфичных для браузера заголовков вроде Sec-CH-UA.
IPv6-прокси выдаёт себя в первую очередь на сетевом слое. Большинство коммерческих пулов сидят в /32 или /48 префиксах, которые целиком принадлежат датацентрам. Антифрод знает: если весь трафик аккаунта идёт из префикса 2a0b:xxxx::/32, зарегистрированного на хостинг-провайдера в Нидерландах, — это не жилой клиент. Даже если каждый запрос идёт с нового /128.
Дальше начинается поведенческий анализ. И вот здесь IPv6-прокси сыпется чаще, чем IPv4. Причина — ротация адресов. У пользователя IPv4-прокси обычно один адрес на сессию, у IPv6-пулов — сотни адресов, между которыми клиент переключается. Это создаёт паттерны, которых не бывает у живых людей.
Ротация /128 внутри /64 — главный маркер
У реального пользователя IPv6-адрес меняется при переподключении к провайдеру. Обычно это privacy extensions (RFC 4941): временный адрес живёт несколько часов или суток, потом меняется. Смена привязана к перезагрузке роутера или таймауту, а не к каждому HTTP-запросу.
IPv6-прокси-пул ведёт себя иначе. Один и тот же аккаунт за 10 минут делает 40 запросов с 40 разных /128 в пределах одного /64. Для антифрода это красный флаг размером с дом. Живой человек физически не может переподключаться 40 раз за 10 минут.
Пример: маркетплейс логирует для каждого запроса полный IPv6-адрес. За сессию из 15 минут с одного аккаунта пришло 23 уникальных /128. Все — в префиксе 2001:41d0:xxx::/48. Провайдер префикса — OVH. Дальше аккаунт попадает в ручной review, потому что комбинация «много /128 + хостинг-ASN» не встречается у реальных покупателей.
Геолокация по BGP-префиксу против геолокации по IP
Классическая ошибка — думать, что геолокация IPv6 работает так же, как IPv4. Работает, но грубее. Базы вроде MaxMind привязывают IPv6 к префиксу, а не к отдельному адресу. Если /48 зарегистрирован за провайдером во Франкфурте, все адреса из него будут показывать Франкфурт — даже если фактический сервер стоит в Сингапуре.
Антифрод это знает и сверяет. Аккаунт заявляет адрес доставки в Москве, платёжный метод — российская карта, Accept-Language: ru-RU, часовой пояс браузера Europe/Moscow. А IPv6-префикс принадлежит Hetzner в Германии. Расхождение между заявленной и сетевой геолокацией — ещё один сигнал.
Хуже, если прокси-пул пытается «подделать» геолокацию, выставляя заголовки. Антифрод видит: X-Forwarded-For указывает на российский адрес, но TCP-соединение пришло из немецкого префикса. Такое расхождение фиксируется и добавляет вес к fraud score.
TLS- и HTTP-фингерпринты, которые не сходятся с IPv6
JA3-фингерпринт строится из версии TLS, набора шифров, порядка расширений и эллиптических кривых. У реального Chrome 124 на Windows 11 один JA3, у curl — другой, у Python requests — третий. Прокси-пул обычно не управляет TLS-слоем клиента, поэтому все аккаунты, сидящие через один пул, приходят с одинаковым JA3.
Если этот JA3 совпадает с fingerprint библиотеки curl или Go-http-client, а User-Agent заявляет Chrome — это несоответствие. Антифрод его ловит тривиально. И вот тут IPv6 усугубляет: пул часто используется автоматизированными скриптами (парсеры цен, sniper-боты), а не людьми через браузер. Поведение выдаёт себя в связке с фингерпринтом.
HTTP/2 fingerprint (SETTINGS-фрейм, порядок псевдозаголовков, размер window update) добавляет ещё один слой. Chrome отправляет один набор параметров, Python httpx — другой. Совпадение HTTP/2-фингерпринта с библиотечным при заявленном браузерном UA — сигнал.
Поведение аккаунта: тайминги, глубина просмотра, конверсия
Антифрод маркетплейсов моделирует не отдельные запросы, а сессии. Живой покупатель: заходит на главную, листает категорию 2-3 минуты, открывает 4-7 карточек, задерживается на каждой 15-40 секунд, иногда добавляет в корзину, иногда уходит. Между действиями — паузы от 1 до 30 секунд, есть скроллы, есть движения мыши.
Аккаунт за IPv6-прокси часто показывает: 200 запросов карточек товаров за 5 минут без пауз, ноль событий мыши, ноль скроллов, одинаковая последовательность страниц у десятка аккаунтов. Это не человек. Даже если каждый запрос идёт с нового /128, паттерн таймингов остаётся.
Отдельный маркер — синхронность между аккаунтами. Если 15 аккаунтов с разных /128 в одном /48 префиксе начинают сессию в течение одной минуты и открывают одни и те же товары в одном порядке — это бот-ферма. Кластеризация по времени старта и по последовательности действий ловит такие группы надёжнее, чем любой IP-чёрный список.
Пример: как выглядит сессия через IPv6-пул в логах
Разберём типичный лог. Аккаунт user_88213, окно 12 минут:
```
12:00:03 2001:41d0:a1b2::1f3 GET /catalog/smartfony
12:00:07 2001:41d0:a1b2::4c7 GET /catalog/smartfony?page=2
12:00:11 2001:41d0:a1b2::9a1 GET /product/12345
12:00:14 2001:41d0:a1b2::b22 GET /product/12346
12:00:18 2001:41d0:a1b2::e04 GET /product/12347
```
Пять запросов, пять разных /128, интервал 3-4 секунды, ровный как часы. У живого пользователя на мобильном IPv6 privacy extensions адрес меняется раз в несколько часов, не между запросами. Здесь же ротация на каждый запрос — это поведение прокси-пула, а не клиента.
Дальше антифрод смотрит на префикс: 2001:41d0::/32 — это OVH. Хостинг. Ни один мобильный оператор и ни один домашний провайдер не выдаёт адреса из этого префикса. Вердикт: прокси. Аккаунт уходит в теневой бан или на капчу.
Как отличить «плохой» IPv6 от «нормального»
Не всякий IPv6 — красный флаг. Мобильные операторы (МТС, Билайн, Vodafone, Deutsche Telekom) выдают клиентам реальные IPv6 из своих eyeball-префиксов. Такие адреса выглядят органично: ASN мобильного оператора, геолокация совпадает с регионом, PTR-запись соответствует пулу провайдера.
Проблема в том, что residential IPv6 дороже и сложнее в поддержке. Пул из /64 у мобильного оператора — это уже не «прокси за копейки», а инфраструктура. Провайдеры вроде lexic.ml держат пулы в eyeball-сетях, где префиксы принадлежат реальным ISP, а не датацентрам — это меняет картину для антифрода, но не отменяет поведенческий анализ.
Таблица сравнения сигналов:
| Сигнал | Датацентр /48 | Eyeball /64 (мобильный ISP) |
|---|---|---|
| ASN | Хостинг (OVH, Hetzner, DigitalOcean) | Мобильный оператор |
| Геолокация | Совпадает с датацентром | Совпадает с регионом клиента |
| PTR | Часто отсутствует | Есть, соответствует пулу ISP |
| Ротация /128 | Высокая, на запрос | Низкая, по таймауту |
| Синхронность аккаунтов | Высокая | Низкая |
Даже с eyeball-префиксом поведение решает. Если с /64 мобильного оператора идут 50 аккаунтов с одинаковыми таймингами — это бот-ферма, и никакой «чистый» префикс её не спасёт.
Код: как антифрод считает ротацию по /64
Простой пример на Python — группировка запросов по /64 и подсчёт уникальных /128 внутри окна. Такой скрипт легко превращается в правило скоринга.
```python
import ipaddress
from collections import defaultdict
from datetime import datetime
def prefix64(ip_str):
ip = ipaddress.ip_address(ip_str)
net = ipaddress.ip_network(f"{ip}/64", strict=False)
return str(net.network_address)
def rotation_score(requests, window_sec=600):
by_account = defaultdict(list)
for r in requests:
by_account[r["account"]].append(r)
scores = {}
for acc, reqs in by_account.items():
reqs.sort(key=lambda x: x["ts"])
max_unique = 0
for i in range(len(reqs)):
start = reqs[i]["ts"]
window = [r for r in reqs[i:] if (r["ts"] - start).total_seconds() <= window_sec]
prefixes = {prefix64(r["ip"]) for r in window}
addrs = {r["ip"] for r in window}
if len(prefixes) == 1:
max_unique = max(max_unique, len(addrs))
scores[acc] = max_unique
return scores
```
Если `max_unique` за 10 минут больше 5 внутри одного /64 — это уже подозрительно. Больше 15 — почти наверняка пул. Порог зависит от платформы, но порядок величин такой.
Код: проверка префикса через BGP-данные
Второй слой — сверка ASN. Используем Team Cymru DNS-интерфейс или локальную копию BGP-таблицы.
```bash
curl -s "https://stat.ripe.net/data/prefix-overview/data.json?resource=2001:41d0:a1b2::1f3" \
| jq '.data.asns[] | {asn: .asn, holder: .holder}'
```
Ответ покажет ASN и владельца. Если holder содержит `OVH`, `Hetzner`, `DigitalOcean`, `Contabo`, `Vultr` — префикс хостинговый. Дальше правило простое: хостинговый ASN + аккаунт с историей покупок в другом регионе = сигнал.
Для массовой проверки лучше держать локальный дамп: `bgpq4` или `pyasn` с обновлением раз в сутки. Запрос к RIPE Stat на каждый адрес — это лишние 100-300 мс и rate limit.
Реальные кейсы из практики
**Кейс с парсером цен.** Маркетплейс электроники, nginx 1.24 + Lua-модуль для логирования. Правило: если с одного аккаунта за 5 минут пришло больше 20 уникальных /128 в одном /64 — стоп. Через это правило за неделю отвалилось 340 аккаунтов. Все — парсеры цен с IPv6-пула в Hetzner (ASN 24940). Ротация шла на каждый запрос. После бана парсеры переехали на residential IPv4, но потеряли скорость в 8 раз — прокси-пул на IPv4 дороже и медленнее.
**Кейс с бот-фермой отзывов.** 60 аккаунтов оставляли отзывы на товары одного продавца. IP — разные /128 в трёх /48 префиксах. Антифрод поймал не по IP: все 60 аккаунтов регистрировались в течение 40 минут, использовали одинаковый набор товаров для «прогрева», а тайминги между регистрацией и первым отзывом совпадали с точностью до 2-3 секунд. Кластеризация по времени + последовательности действий. IP был лишь одним из признаков.
**Кейс с «умным» пулом.** Провайдер ротировал /128 раз в 30 минут, имитируя privacy extensions. Аккаунты выглядели прилично по IP. Но антифрод заметил: смена /128 происходила ровно в :00 и :30 каждой сессии, у всех аккаунтов одновременно. Privacy extensions так себя не ведут — они привязаны к uptime интерфейса, а не к глобальному таймеру. Синхронность ротации выдала пул. Решение провайдера — рандомизировать интервал с джиттером ±7 минут, но часть аккаунтов к тому моменту уже была забанена.
Что делать, если аккаунт уже под подозрением
Первое — перестать ротировать /128 на каждый запрос. Стабильный адрес на сессию выглядит органичнее, чем 40 адресов за 10 минут. Второе — привести TLS- и HTTP-фингерпринты в соответствие с заявленным браузером. curl с UA Chrome — это провал на первом же запросе.
Третье — тайминги. Рандомизировать паузы между запросами, добавить события мыши и скроллов, не открывать 200 карточек за минуту. Антифрод маркетплейсов моделирует поведение, а не только сеть. Прокси без поведенческой маскировки — это половина решения.
Четвёртое — следить за консистентностью. Часовой пояс браузера, Accept-Language, геолокация префикса и адрес доставки должны указывать в одну сторону. Расхождение по любому из этих параметров добавляет вес к fraud score, и сумма весов быстро переваливает порог.
IPv6-прокси не «палится» по адресу. Он палится по тому, как аккаунт через него ходит. Ротация, тайминги, фингерпринты, синхронность с другими аккаунтами — вот где антифрод ищет сигналы. Адрес лишь даёт контекст: хостинг или eyeball, совпадает ли геолокация, есть ли PTR. Дальше решает поведение.