← Назад в базу знаний

Как антифрод-системы маркетплейсов вычисляют IPv6-прокси по паттернам портов и TTL

Как антифрод-системы маркетплейсов вычисляют IPv6-прокси по паттернам портов и TTL

Маркетплейсы научились ловить прокси не по чёрным спискам IP, а по поведению стека. Адрес меняется, подсеть меняется, а TCP-отпечаток и TTL остаются. Разбираю, как антифрод строит сигнатуры и почему IPv6 тут даёт больше зацепок, чем IPv4.

Почему IPv6-прокси палится легче

В IPv4 пул адресов у провайдера прокси — это одна-две /24. Антифрод их быстро заносит в базу. В IPv6 провайдер выдаёт /64 на клиента, и формально у тебя 2^64 адресов. Казалось бы, рай для скрытности.

На практике всё наоборот. Антифрод смотрит не на отдельный адрес, а на префикс /64 и /48. Если с одной /64 за сутки пришло 800 регистраций с разных «устройств» — это не 800 человек. Это один прокси-пул. И тут вскрываются детали, которые в IPv4 никто не замечал: как именно устроены соединения внутри этой подсети.

Плюс IPv6 почти всегда означает конкретный хостинг. Жилой провайдер даёт /56 или /48, но если префикс принадлежит AS хостера (Hetzner, OVH, DigitalOcean) — вопрос закрыт. Антифрод сверяет BGP-анонс префикса с типом ASN за 3 мс.

TTL как отпечаток операционной системы

TTL (Time To Live) в IPv6-заголовке — это Hop Limit. Дефолтные значения по ОС: Linux — 64, Windows — 128, macOS — 64, Cisco IOS — 255. Пакет проходит через N маршрутизаторов, каждый уменьшает счётчик на 1.

Если прокси стоит на Linux-сервере в Германии, а клиент подключается из Москвы, исходный Hop Limit 64 пройдёт через 10-15 хопов и придёт как 49-54. Антифрод видит: браузер заявляет Windows 11 (User-Agent, Client Hints), а Hop Limit соответствует Linux-серверу. Несоответствие.

Есть нюанс. Многие прокси-решения нормализуют TTL, чтобы он выглядел «как у клиента». Но нормализация сама по себе — паттерн. Если у 500 сессий из одной /64 ровно Hop Limit 64 — это не 500 разных маршрутов до клиента, это один сервер с настроенным mangle.

Вот как выглядит проверка на стороне сервиса:

```python

import ipaddress

EXPECTED_HOP_LIMIT = {

"windows": 128,

"linux": 64,

"macos": 64,

"android": 64,

"ios": 64,

}

def check_hop_limit(observed_ttl, claimed_os, hop_estimate=0):

base = EXPECTED_HOP_LIMIT.get(claimed_os)

if base is None:

return None

expected = base - hop_estimate

delta = abs(observed_ttl - expected)

if delta > 8:

return f"ttl_mismatch: observed={observed_ttl} expected~{expected}"

if observed_ttl == base:

return "ttl_not_decremented: possible normalization"

return None

```

Порог 8 хопов — эмпирика. Меньше — много ложных срабатываний на мобильных операторах с длинными маршрутами. Больше — пропускаешь явные прокси.

Паттерны портов источника

Тут начинается интересное. Прокси-серверы (особенно SOCKS5 и HTTP CONNECT) при исходящих соединениях используют эфемерные порты из диапазона ядра. Linux по умолчанию берёт из `ip_local_port_range` — обычно 32768-60999. Windows — 49152-65535. macOS — 49152-65535.

Если маркетплейс видит, что все запросы из /64 приходят с source port в диапазоне 32768-60999 и при этом User-Agent заявляет Windows — сигнал. Windows-машина физически не может выдать такой порт. Это либо Linux-сервер напрямую, либо прокси на Linux.

Второй паттерн — последовательность портов. Ядро Linux выдаёт эфемерные порты по алгоритму, близкому к последовательному с шагом. Если собрать 20 соединений из одной /64 за минуту и увидеть source ports 41234, 41236, 41238, 41240 — это один NAT-шлюз с предсказуемой аллокацией. Живые клиенты так себя не ведут: у них порты разбросаны по всему диапазону, потому что между соединениями проходят часы и тысячи других сокетов.

Собрать статистику можно так:

```python

from collections import Counter

def analyze_port_distribution(sessions):

ports = [s["src_port"] for s in sessions]

if len(ports) < 10:

return None

in_linux_range = sum(1 for p in ports if 32768 <= p <= 60999)

in_win_range = sum(1 for p in ports if 49152 <= p <= 65535)

ratio_linux = in_linux_range / len(ports)

deltas = [b - a for a, b in zip(sorted(ports), sorted(ports)[1:])]

common_delta, freq = Counter(deltas).most_common(1)[0]

sequential = freq / len(deltas) > 0.4 and common_delta < 10

flags = []

if ratio_linux > 0.85:

flags.append(f"linux_port_range:{ratio_linux:.2f}")

if sequential:

flags.append(f"sequential_ports:delta={common_delta}")

return flags

```

Порог 85% выбран не случайно: живые пользователи на Linux тоже дают порты из этого диапазона, но у них разброс шире из-за долгих сессий и фоновых приложений.

Согласованность TCP-опций

TTL и порты — только часть картины. Прокси-серверы часто не пересобирают TCP-стек, а просто пересылают поток. Значит, TCP options (MSS, window scaling, SACK permitted, timestamps) приходят от серверной ОС, а не от клиентской.

Типичная сигнатура Linux-сервера: MSS 1460, window scaling factor 7, timestamps включены, SACK permitted. Windows 11 даёт MSS 1460, но window scaling 8 и другие timestamps. macOS — MSS 1440 (из-за другого MTU на некоторых интерфейсах).

Антифрод строит отпечаток из комбинации: TTL + TCP options + порядок опций. Это то, что делает p0f. Если отпечаток говорит Linux 5.x, а браузер заявляет Windows — прокси.

Кейс: пул /64 с одинаковым Hop Limit

Маркетплейс электроники, 40 тысяч регистраций в сутки. Антифрод заметил аномалию: за 6 часов с префикса 2a01:4f8:1c1c::/64 пришло 340 регистраций. Все с разными IPv6-адресами внутри /64, разные User-Agent, разные устройства по Client Hints.

Причина вскрылась при анализе Hop Limit. У всех 340 сессий observed_ttl был ровно 57. Ни одного отклонения. Реальные клиенты с таким же заявленным гео (Германия) давали разброс 54-58, потому что маршруты разные. Ровное значение 57 у всех — это нормализация на прокси-сервере: сервер ставил фиксированный TTL 64 на выходе, до маркетплейса доходило 57 хопов по магистрали.

Решение: добавили сигнал `ttl_variance < 1` для групп из более чем 50 сессий с одного /64. Ложных срабатываний на легитимных корпоративных NAT — 0.3%, потому что там TTL всё равно варьируется из-за разных путей до шлюза.

Кейс: последовательные порты из SOCKS5

Сервис бронирования, всплеск фейковых отзывов. 120 отзывов за час, все с разных IPv6 из /48, принадлежащего одному хостеру. Антифрод сначала смотрел на ASN — это сработало, но не объясняло, почему часть отзывов прошла с residential-прокси.

Копнули глубже. У прошедших отзывов source ports шли с шагом 2: 38210, 38212, 38214… Это классика Linux `__inet_hash_connect` при быстром открытии сокетов из одного процесса. Residential-прокси на деле оказался цепочкой: клиент → SOCKS5 на VPS → residential-выход. Порты на первом хопе выдавали VPS.

Решение: сигнал `sequential_ports` с весом 0.4 в скоринге. Порог срабатывания — 15+ соединений с шагом меньше 10 за 60 секунд с одной /64. Точность выросла: отсекли 92% фейков, потеряли 1.1% легитимных (в основном корпоративные API-клиенты, которые тоже открывают сокеты пачками).

Кейс: несоответствие MTU и MSS

Площадка с объявлениями. Прокси-пул на OVH, IPv6, разные адреса. Антифрод поймал по MSS. У всех сессий из /64 MSS был 1440, а не 1460. Это значит, что где-то в пути MTU 1480 вместо стандартных 1500 — типично для туннелей (GRE, IPIP) или PPPoE.

Проблема: заявленный клиент — мобильный Android в сети оператора, где MTU обычно 1500 или 1400 (для LTE с GTP-туннелями). MSS 1440 не бьётся ни с одним типичным профилем. Это выдало промежуточный туннель на стороне прокси.

Решение: добавили проверку MSS против заявленного типа сети. Для Wi-Fi — ожидание 1460, для LTE — 1400 или 1460, для 5G — 1420-1460. Отклонение больше 40 байт — сигнал. В этом кейсе отсекли пул за 2 дня, до того как он успел нагадить.

Что делать провайдеру прокси

Если ты держишь пул и хочешь жить — нужно нормализовать не только TTL, но и весь стек. Проблема в том, что нормализация сама становится сигнатурой, если делать её грубо.

Рабочий подход — рандомизация в пределах правдоподобного. TTL не фиксировать, а выставлять `base - random(8, 14)`, чтобы имитировать реальный путь. Порты — не полагаться на ядро, а биндить сокеты на случайные из диапазона, соответствующего заявленной ОС. MSS — подстраивать под профиль клиента.

Сервис lexic.ml с 2015 года крутит IPv6-прокси и на практике знает: клиенты, которые не заморачиваются со стеком, выгорают за дни. Те, кто нормализует — живут месяцами, но требуют индивидуальной настройки под каждую площадку.

Как антифрод собирает сигналы в скоринг

По отдельности каждый сигнал слабый. TTL-несоответствие бывает у 3-5% легитимных пользователей за корпоративным NAT. Последовательные порты — у API-клиентов. MSS-аномалия — у мобильных операторов с туннелями.

Сила в комбинации. Антифроды используют взвешенную сумму: TTL mismatch 0.3, port range mismatch 0.25, sequential ports 0.4, MSS mismatch 0.2, ASN hosting 0.5. Порог блокировки — 0.7-0.8. Один сигнал не блокирует, два-три — да.

Плюс временное окно. Сигналы считаются не по одной сессии, а по группе за окно 5-60 минут. Это отсекает шум и ловит именно паттерны пула.

Ограничения метода

TTL врёт на мобильных сетях. Операторы часто используют CGNAT с непредсказуемым числом хопов. Hop Limit может прийти 40 или 55 — и это нормально.

Порты врёт на IPv6 с privacy extensions. Ядро всё равно выдаёт эфемерные из своего диапазона, но при долгих сессиях и NAT64 картина размывается.

MSS врёт везде, где есть туннели. А их сейчас много: WireGuard, Tailscale, корпоративные VPN.

Так что антифрод никогда не блокирует по одному сигналу. Только по совокупности, и всегда с ручной проверкой для пограничных случаев.

Итог для тех, кто строит защиту

Смотри на стек, а не на адреса. IPv6 даёт миллиарды адресов, но стек у всех одинаковый по ОС. Собирай сигнатуры: Hop Limit, TCP options, MSS, source ports. Считай не по одной сессии, а по /64 за окно. Комбинируй сигналы в скоринг, не блокируй по одному.

И помни: нормализация стека на стороне прокси — это тоже сигнал. Ровные значения подозрительнее разбросанных.

✔️Купить прокси