Как антифрод-системы маркетплейсов вычисляют ротацию IPv6 по смене ASN при неизменном fingerprint
Содержание
- Почему ASN стал важнее самого адреса
- Что такое fingerprint и почему он не так прост
- Механика корреляции ASN и fingerprint
- Python: корреляция ASN и fingerprint по логам
- curl: проверка собственного ASN и префикса
- Таблица: типичные ASN и их поведение
- Nginx: логирование ASN на входе
- Кейс: маркетплейс и пул из 200 IPv6
- Кейс: мобильный оператор и ложные срабатывания
- Кейс: подмена ASN через BGP-анонсы
- Что реально ловит ротацию
- Практические выводы для обеих сторон
Антифрод на маркетплейсах давно перестал смотреть только на IP. Смена адреса в лоб ничего не говорит — половина легитимных пользователей сидит за мобильным оператором, где IPv6-префикс меняется каждые несколько часов. Интереснее другое: когда fingerprint устройства остаётся идентичным, а автономная система под ним прыгает как блоха. Вот тут начинается детект.
Почему ASN стал важнее самого адреса
IPv6-адрес в /64 — это не идентификатор пользователя. Это идентификатор подсети. Провайдер выдаёт клиенту целый /56 или /48, и внутри него может быть 65 536 адресов. Антифрод-движки это знают и давно агрегируют адреса до уровня префикса и ASN, а не работают с отдельными /128.
ASN — номер автономной системы. Он присвоен конкретному оператору, хостингу, дата-центру. У Comcast это AS7922, у Hetzner — AS24940, у DigitalOcean — AS14061. Когда пользователь сидит на домашнем интернете, ASN стабилен неделями. Когда трафик идёт через пул прокси, ASN меняется от запроса к запросу.
Именно эта корреляция — неизменный fingerprint плюс пляшущий ASN — даёт антифроду сигнал высокой точности. Легитимный пользователь не может физически оказаться в AS24940 и AS9009 в течение одной минуты с одного браузера.
Что такое fingerprint и почему он не так прост
Fingerprint — это не только User-Agent. Современный отпечаток собирается из десятков параметров: canvas hash, WebGL vendor и renderer, список шрифтов, разрешение экрана, часовой пояс, navigator.platform, аппаратные concurrency, глубина цвета, наличие touch-событий, TLS-отпечаток (JA3/JA4), HTTP/2 SETTINGS frame, порядок заголовков.
Canvas hash считается через отрисовку текста и графики в скрытый canvas — разные GPU и драйверы дают разные пиксели. WebGL renderer выдаёт модель видеокарты строкой вроде `ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0)`. JA3 — это MD5 от конкатенации версии TLS, списка шифров, расширений и эллиптических кривых в ClientHello.
Антифрод считает композитный хеш. Если он совпадает между сессиями, а ASN разный — это красный флаг. Настоящий пользователь с одного устройства не телепортируется между операторами.
Механика корреляции ASN и fingerprint
Движок хранит временной ряд: для каждого fingerprint — список пар (timestamp, ASN, prefix). Дальше применяется простая логика. Если за окно в 10 минут fingerprint F встретился в ASN A, B и C, причём эти ASN не связаны (разные владельцы, разные гео), — сессия помечается.
Сложнее, когда прокси-пул подобран грамотно: все адреса в одной AS, скажем, у одного хостера. Тогда смены ASN нет, и детект не срабатывает. Но тут в игру вступает геолокация префикса и BGP-маршрутизация — антифрод сверяет ASN с ожидаемым регионом пользователя по другим сигналам.
Ещё один слой — проверка, является ли ASN хостинговым. Базы вроде IP2Location и MaxMind помечают тип сети: ISP, hosting, business, education. Если fingerprint домашнего пользователя (шрифты Windows 10, часовой пояс Europe/Moscow) вдруг светится из хостинговой AS — это несоответствие.
Python: корреляция ASN и fingerprint по логам
Вот скрипт, который берёт лог запросов и ищет fingerprint'ы, прыгающие между ASN.
```python
import sqlite3
from collections import defaultdict
from datetime import datetime, timedelta
conn = sqlite3.connect("requests.db")
cur = conn.cursor()
cur.execute("""
SELECT fingerprint, asn, ts FROM requests
WHERE ts > ?
""", (datetime.utcnow() - timedelta(hours=1),))
by_fp = defaultdict(list)
for fp, asn, ts in cur.fetchall():
by_fp[fp].append((ts, asn))
def parse_ts(s):
return datetime.fromisoformat(s)
suspicious = []
for fp, events in by_fp.items():
events.sort()
window = []
for ts, asn in events:
t = parse_ts(ts)
window = [(wt, wa) for wt, wa in window if t - wt < timedelta(minutes=10)]
window.append((t, asn))
asns = {a for _, a in window}
if len(asns) >= 3:
suspicious.append((fp, asns, len(window)))
break
for fp, asns, n in suspicious:
print(f"{fp[:16]}... ASN={sorted(asns)} hits={n}")
```
Логика простая: скользящее окно 10 минут, если внутри него у одного отпечатка набралось три разных ASN — помечаем. Порог в три ASN выбран не случайно: два ASN ещё можно списать на мобильного оператора с двумя аплинками, три — уже нет.
curl: проверка собственного ASN и префикса
Перед тем как гонять трафик, полезно понимать, что о вас видно снаружи. Минимальный набор — ASN, префикс, тип сети.
```bash
curl -s "https://ipinfo.io/json" | jq '{ip, org, country, region}'
curl -s "https://api.bgpview.io/ip/\$(curl -s ifconfig.me)" | jq '.data.prefixes[0]'
```
Первая команда отдаёт ASN в поле `org` строкой вида `AS24940 Hetzner Online GmbH`. Вторая — префикс, из которого видно, насколько широкий блок вам выдали. Если это /64 от хостера и он один на весь пул — антифрод склеит все сессии в одну группу по prefix.
Полезно ещё проверять rDNS. У residential-провайдеров PTR обычно содержит название оператора и город. У хостингов — что-то вроде `static.249.40.10.140.clients.your-server.de`. Антифрод смотрит на это как на дополнительный сигнал.
Таблица: типичные ASN и их поведение
| ASN | Владелец | Тип | Смена префикса | Типичный PTR |
|-----|----------|-----|----------------|--------------|
| AS7922 | Comcast | ISP | раз в сутки | c-XX-XX-XX-XX.hsd1.xx.comcast.net |
| AS24940 | Hetzner | Hosting | статичен | static.X.X.X.X.clients.your-server.de |
| AS14061 | DigitalOcean | Hosting | статичен | без PTR или droplet |
| AS9009 | M247 | Hosting/VPN | статичен | часто пусто |
| AS8359 | MTS | ISP | раз в сессию | динамический |
Разница между ISP и Hosting — ключевая. Антифрод-правило «домашний fingerprint + hosting ASN = блок» ловит большинство неаккуратных прокси-схем.
Nginx: логирование ASN на входе
Чтобы корреляция работала, ASN нужно получить на этапе запроса. GeoIP2-модуль в nginx умеет отдавать ASN напрямую, если подгрузить базу GeoLite2-ASN.
```nginx
http {
geoip2 /etc/nginx/GeoLite2-ASN.mmdb {
auto_reload 60m;
\$geoip2_data_asn autonomous_system_number;
\$geoip2_data_asn_org autonomous_system_organization;
}
log_format af '\$remote_addr - \$geoip2_data_asn '
'"\$http_user_agent" "\$http_x_fingerprint" '
'ja3=\$http_ssl_ja3 rt=\$request_time';
server {
listen 443 ssl http2;
access_log /var/log/nginx/af.log af;
}
}
```
Ключевой момент — заголовок `X-Fingerprint`, который фронтенд прокидывает с клиента. Без него корреляция невозможна: nginx видит только сетевые параметры. JA3 берётся из переменной `\$http_ssl_ja3`, если модуль SSL настроен на его вычисление.
Кейс: маркетплейс и пул из 200 IPv6
Разбираем реальную схему. Маркетплейс с 4 млн MAU, антифрод на базе собственного движка плюс Sift Science. Кто-то запустил парсинг цен через пул из 200 IPv6-адресов, все в AS24940 (Hetzner), fingerprint браузера зафиксирован через антидетект-браузер.
Проблема вылезла на третий день. Движок заметил: один fingerprint, 47 разных /128, но все в одном /64-префиксе `2a01:4f8:xxxx::/64`. Антифрод не стал ждать смены ASN — он агрегировал по префиксу. Все 47 адресов схлопнулись в одну сущность, и сессия получила risk score 87 из 100.
Причина — недооценка того, что /64 воспринимается как единица. Хостер выдаёт клиенту /64 целиком, и все адреса внутри считаются одним «местом». Решение у атакующей стороны — распределять по разным /64 и разным ASN, но это резко дороже. У защищающейся стороны решение тривиально: агрегировать до /64 и до ASN одновременно.
Кейс: мобильный оператор и ложные срабатывания
Обратная ситуация. Легитимные пользователи одного российского оператора (AS8359) начали получать блокировки при входе в аккаунт. Логи показали: fingerprint совпадает, ASN один, но префикс меняется каждые 20-40 минут — оператор использует CGNAT и динамическую выдачу /64.
Антифрод ошибочно трактовал смену префикса как признак прокси. Причина — правило «смена префикса при том же fingerprint = подозрительно» без учёта типа ASN. Для residential ISP это нормальное поведение, для hosting — аномалия.
Решение: добавить в правило условие на тип ASN. Если `network_type == 'isp'` и смена префикса происходит в пределах одной AS — снижать вес сигнала до нуля. Если `network_type == 'hosting'` — оставлять высокий вес. После правки ложные срабатывания по этому оператору упали с 12% до 0.4%.
Кейс: подмена ASN через BGP-анонсы
Экзотика, но встречается. Пул прокси арендовал блок IPv6 у мелкого регионального оператора, анонсировал его через свой AS. Формально адреса residential, ASN — маленького ISP, fingerprint домашний. Идеальная маскировка.
Антифрод поймал на другом. RPKI-валидация показала, что origin AS не совпадает с тем, кому реально делегирован блок. Плюс traceroute от сервера до клиента упирался в дата-центр во Франкфурте, хотя геолокация префикса указывала на Владивосток. Расхождение гео и сетевого пути — сигнал.
Цифры: RTT от сервера маркетплейса (Москва) до заявленного Владивостока — 8 мс. Реальный RTT до Владивостока — минимум 90-110 мс. Такое расхождение видно по TCP handshake и не спрятать. Антифрод-движок считает медиану RTT по fingerprint и сравнивает с ожидаемой для гео.
Что реально ловит ротацию
Сводка сигналов, которые работают в связке. По отдельности каждый даёт ложные срабатывания, вместе — точность выше 95%.
Первый — смена ASN при неизменном fingerprint за короткое окно. Второй — hosting-тип ASN при residential-отпечатке. Третий — агрегация по /64 и /48, а не по отдельным адресам. Четвёртый — расхождение RTT и геолокации префикса. Пятый — RPKI-валидация origin AS.
Прокси-сервисы вроде lexic.ml дают ротацию на уровне /64 внутри одной AS, что снижает видимость смены ASN, но не отменяет агрегацию по префиксу. Если антифрод настроен на работу с /64 как с единицей, ротация внутри блока не помогает — все адреса схлопываются в один идентификатор.
Практические выводы для обеих сторон
Для защищающейся стороны: не смотрите на /128. Агрегируйте до /64 минимум, лучше до /48 и ASN. Храните временные ряды по fingerprint, а не только текущее состояние. Считайте RTT и сверяйте с гео. Валидируйте origin AS через RPKI.
Для атакующей стороны (в исследовательских целях): ротация внутри одной AS бесполезна против агрегации по префиксу. Нужны разные ASN, желательно residential-типа, с корректным RPKI и совпадающим RTT. Каждый из этих пунктов повышает стоимость схемы на порядок.
Композитный fingerprint сам по себе — не приговор. Приговор — его неизменность в сочетании с сетевой нестабильностью. Антифрод-движки последних поколений строят графы: узлы — fingerprint, ASN, префикс, RTT-кластер; рёбра — совстречаемость в сессиях. Аномалия видна на уровне топологии графа, а не отдельных признаков.