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

Почему антифрод-системы маркетплейсов палят IPv6 прокси по TTL и MSS: технический разбор

Почему антифрод-системы маркетплейсов палят IPv6 прокси по TTL и MSS: технический разбор

Что вообще проверяет антифрод на транспортном уровне

Большинство статей про палево прокси крутится вокруг HTTP-заголовков: User-Agent, Accept-Language, порядок заголовков. Это уровень L7, и его антифрод-системы маркетплейсов давно научились нормализовать. Настоящая мясорубка начинается ниже — на L3 и L4, куда разработчики прокси-сервисов лезут неохотно, потому что там нельзя просто подменить строку.

Когда вы подключаетесь к маркетплейсу через IPv6-прокси, TCP-сессия проходит через цепочку: ваш клиент → прокси → целевой сервер. Каждый хоп этой цепочки оставляет отпечатки, которые нельзя убрать заголовком. TTL пакета, размер MSS в SYN, окно TCP, опции TCP — всё это формируется ядром операционной системы и маршрутизаторами по пути. Антифрод маркетплейса видит не ваш браузер, а совокупность этих параметров на входе своего балансировщика.

Проблема IPv6 в том, что адресное пространство огромное, но структура аллокации предсказуемая. Провайдеры выдают /64 или /56 префиксы, и эти префиксы зарегистрированы в RIR. Антифрод знает: этот /48 принадлежит хостингу, этот /64 — residential ISP. И когда с одного /64 приходит 500 сессий с одинаковым TTL и одинаковым MSS — это уже не статистика, это подпись прокси-пула.

TTL как маркер расстояния и его подмена

TTL (Time To Live) в IPv6 называется Hop Limit. Поле 8 бит, максимум 255. Каждый маршрутизатор уменьшает его на единицу. Когда счётчик доходит до нуля — пакет дропается, отправителю уходит ICMPv6 Time Exceeded.

Начальное значение Hop Limit задаёт ядро отправителя:

- Linux: 64

- Windows: 128

- Cisco IOS: 255

- Некоторые BSD: 64

Целевой сервер видит не начальный TTL, а остаточный. Если от клиента до сервера 12 хопов, Linux-пакет придёт с TTL=52. Windows-пакет — с 116. Антифрод строит карту: какие значения TTL типичны для residential-подключений в этом регионе, какие — для датацентров.

Прокси-сервер, который просто форвардит пакеты, не меняет Hop Limit. Клиент с Linux отправил TTL=64, прокси пробросил, сервер получил 64 минус хопы от прокси до сервера. Если прокси в датацентре во Франкфурте, а маркетплейс в Москве — это 15-20 хопов. Остаточный TTL будет 44-49. Для residential-клиента из Москвы с тем же Linux — 5-7 хопов, TTL 57-59.

Разница в 10 единиц — это уже сигнал. А если с одного IPv6-адреса приходят сессии с TTL 44, 45, 46, 47 — антифрод понимает, что за адресом сидит пул, и хопы до разных клиентов разные.

Подмена TTL делается на уровне ядра через iptables/nftables или на уровне прокси-демона. В nftables:

```bash

nft add table ip6 mangle

nft add chain ip6 mangle prerouting '{ type filter hook prerouting priority mangle; }'

nft add rule ip6 mangle prerouting tcp dport 443 counter ttl set 64

```

Но тут грабли: если вы ставите фиксированный TTL=64, а реальный клиент был Windows с TTL=128, вы создаёте новую аномалию. Антифрод видит: с этого /64 все сессии приходят с TTL=64, хотя User-Agent говорит Windows. Несоответствие. Нужна карта соответствия OS → TTL, и она должна применяться по сессии, а не глобально.

MSS и почему он выдаёт ядро прокси

MSS (Maximum Segment Size) — опция TCP, объявляется в SYN-пакете. Это максимальный размер payload сегмента, который отправитель готов принять. MSS = MTU - 40 для IPv6 (20 байт IPv6-заголовок + 20 байт TCP-заголовок).

Стандартные значения:

- Ethernet MTU 1500 → MSS 1460

- PPPoE MTU 1492 → MSS 1452

- IPv6 туннель (6in4) MTU 1480 → MSS 1440

- WireGuard MTU 1420 → MSS 1380

Прокси-серверы часто работают в контейнерах или через туннели. Docker по умолчанию ставит MTU 1500 на bridge, но если хост в WireGuard — MTU 1420, и MSS будет 1380. Антифрод маркетплейса видит: клиент из residential-сети с MSS 1380 — это нетипично. Residential обычно 1460 или 1452.

Ещё хуже: если прокси-сервер не делает MSS clamping, а клиент за туннелем — может случиться PMTU blackhole. SYN проходит, данные не идут. Прокси-сервис ставит MSS clamping на границе, но ставит его криво — например, фиксирует 1360 для всех. Тогда все сессии через этот прокси имеют MSS 1360, независимо от реального клиента. Это отпечаток.

Правильный подход — MSS clamping по интерфейсу с учётом реального MTU:

```bash

nft add rule ip6 mangle prerouting tcp flags syn tcp option maxseg size set rt mtu

```

Это устанавливает MSS в зависимости от MTU маршрута. Но `rt mtu` работает не везде — в некоторых ядрах требует CONFIG_IPV6_ROUTE_INFO. Проверять надо на конкретной версии.

Комбинация TTL + MSS + окно TCP

По отдельности TTL и MSS — слабые сигналы. В комбинации — сильные. Антифрод маркетплейса строит fingerprint по тройке: TTL, MSS, TCP window size.

Типичные профили:

- Windows 10/11: TTL 128, MSS 1460, window 64240

- Linux 5.x: TTL 64, MSS 1460, window 65495 (с window scaling)

- macOS: TTL 64, MSS 1460, window 65535

- Android: TTL 64, MSS 1460, window 65535

Если прокси-сервер на Linux форвардит трафик Windows-клиента, но не трогает TTL — сервер видит TTL 128 минус хопы. Если трогает TTL, но не трогает window — видит Linux-window с Windows-TTL. Антифрод ловит несоответствие.

Плюс есть TCP timestamps, SACK, window scaling factor. Всё это в SYN и SYN-ACK. Полный fingerprint — это 8-12 параметров. Подделать все на прокси-сервере можно, но это уже не прокси, а полноценный TCP-стек с эмуляцией.

Как IPv6 усугубляет ситуацию по сравнению с IPv4

В IPv4 адресное пространство 4.3 млрд, и пулы прокси-сервисов часто используют residential IP — реальные адреса домашних пользователей. Антифрод видит: адрес принадлежит Ростелекому, TTL 57, MSS 1452 — похоже на реального клиента.

В IPv6 адресное пространство 3.4×10^38, но аллокация структурирована. Провайдер выдаёт /56 или /64. Прокси-сервис арендует /48 у хостинга и раздаёт /64 клиентам. Антифрод видит: с /64 приходит 200 сессий. В IPv4 это было бы 200 разных /32 — выглядит как 200 клиентов. В IPv6 это один /64 — выглядит как один клиент с 200 сессиями. Это палево.

Плюс IPv6-префиксы хостингов зарегистрированы в RIR с флагом "hosting". Антифрод маркетплейса делает whois-запрос к RIR, видит "NetName: HOSTING-XX", и помечает весь /48 как датацентр. Residential-прокси на IPv6 почти не существует — домашние провайдеры выдают /56, но не продают их для прокси.

Пример: как выглядит палево в логах балансировщика

Возьмём nginx 1.24 с модулем stream и логированием на уровне TCP. Настраиваем логирование TTL и MSS через переменные:

```nginx

stream {

log_format tcp_basic '\$remote_addr:\$remote_port '

'\$time_iso8601 '

'\$bytes_sent \$bytes_received '

'\$session_time '

'\$upstream_addr';

server {

listen [::]:443;

proxy_pass backend;

access_log /var/log/nginx/tcp.log tcp_basic;

}

}

```

Nginx не отдаёт TTL и MSS напрямую. Их надо снимать через eBPF или tcpdump. Пример на bpftrace:

```bash

bpftrace -e '

kprobe:tcp_v6_rcv {

\$skb = (struct sk_buff *)arg0;

\$iph = (struct ipv6hdr *)(\$skb->data + \$skb->network_header);

\$tcph = (struct tcphdr *)(\$skb->data + \$skb->transport_header);

if (\$tcph->syn == 1 && \$tcph->ack == 0) {

printf("%s TTL=%d MSS=%d WIN=%d\n",

ntop(\$iph->saddr),

\$iph->hop_limit,

\$tcph->doff,

\$tcph->window);

}

}'

```

Это даёт поток SYN-пакетов с TTL, размером TCP-заголовка (косвенно MSS через опции) и окном. Прогоняем через этот скрипт трафик с прокси-пула — и видим кластеры. Если 500 сессий с одного /64 имеют TTL 44-49 и MSS 1380 — это прокси за туннелем.

Кейс: маркетплейс с 50 000 сессий в час

Маркетплейс электроники, балансировщик на базе Envoy 1.28, антифрод на собственном стеке. Пул прокси-сервиса — 2000 IPv6-адресов из /48, арендованного у Hetzner. Прокси на Debian 12, ядро 6.1, форвардинг через nftables без изменения TTL.

Первая неделя: антифрод пропускает 80% трафика. Вторая неделя: палево растёт, блокировки 60%. Причина — антифрод накопил статистику по /48. Все 2000 адресов имеют одинаковый TTL после прохождения хопов до балансировщика: 47 (Hetzner → маркетплейс 17 хопов, начальный TTL 64). MSS 1460, окно 65495. Это Linux-профиль, но User-Agent в 70% случаев Windows.

Решение прокси-сервиса: добавить подмену TTL и MSS на уровне nftables с картой по сессии. Но карта требует знания OS клиента, а прокси видит только TCP-сессию. Пришлось делать эвристику по первому HTTP-запросу: если User-Agent Windows — ставить TTL 128 и MSS 1460, если Linux — 64 и 1460. Задержка на первом пакете выросла на 3-5 мс, но палево упало до 15%.

Кейс: residential-прокси на IPv6 и его провал

Сервис residential-прокси арендовал /56 у немецкого ISP, раздал /64 клиентам. Клиенты — реальные пользователи с домашними роутерами. Проблема: роутеры часто имеют MTU 1492 (PPPoE), MSS 1452. Но прокси-сервер на входе делал MSS clamping до 1440 (запас под туннель). Все сессии приходили с MSS 1440, хотя реальные клиенты имели 1452.

Антифрод маркетплейса заметил: с /56 приходит 300 сессий с MSS 1440, TTL 61-63. Для residential это нетипично — обычно MSS 1452. Плюс TTL 61-63 означает 1-3 хопа от клиента до сервера, а residential-клиенты разбросаны по стране, хопов должно быть 5-15. Вывод: это не residential, это прокси-ферма, эмулирующая residential.

Решение: убрать MSS clamping, пробросить реальный MSS клиента. Но тогда PMTU blackhole на туннелях. Пришлось ставить clamping только для клиентов с MTU < 1400, а для остальных пробрасывать. Сложность выросла, но палево упало.

Кейс: IPv6-префикс и геолокация

Маркетплейс с антифродом на базе MaxMind GeoIP2. Прокси-сервис арендовал /48 в Нидерландах, но клиенты — из России. Антифрод видит: IPv6-адрес из NL, TTL 50 (10 хопов от NL до RU), MSS 1460. User-Agent русский, Accept-Language ru-RU, но IP нидерландский.

Первая линия антифрода — геолокация по IP. Прокси-сервис ставит IPv6-адреса, зарегистрированные в RU. Но RIR-регистрация не совпадает с физическим расположением. Антифрод делает traceroute: видит, что первый хоп после прокси — Amsterdam. Геолокация по traceroute точнее, чем по whois.

Решение: использовать IPv6-адреса, физически расположенные в RU. Но российские хостинги редко продают /48 для прокси. Приходится брать /64 у мелких ISP, которые не засвечены в базах антифрода. Это гонка вооружений.

Практика: как проверить свой прокси на палево

Собираем fingerprint на стороне сервера. Запускаем tcpdump на балансировщике, парсим SYN-пакеты:

```bash

tcpdump -i eth0 -nn -c 1000 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0' -w syn.pcap

tshark -r syn.pcap -T fields -e ipv6.src -e ipv6.hlim -e tcp.options.mss_val -e tcp.window_size_value

```

Получаем таблицу. Группируем по /64 префиксу. Если видим кластер с одинаковым TTL и MSS — это прокси. Дальше сравниваем с эталонным профилем реального клиента из целевого региона.

Python-скрипт для агрегации:

```python

import subprocess

from collections import defaultdict

def get_syn_fingerprints(iface, count=1000):

cmd = [

'tshark', '-i', iface, '-c', str(count),

'-f', 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0',

'-T', 'fields',

'-e', 'ipv6.src', '-e', 'ipv6.hlim',

'-e', 'tcp.options.mss_val', '-e', 'tcp.window_size_value'

]

out = subprocess.check_output(cmd, text=True)

clusters = defaultdict(list)

for line in out.strip().split('\n'):

parts = line.split('\t')

if len(parts) < 4:

continue

src, ttl, mss, win = parts

prefix = ':'.join(src.split(':')[:4])

clusters[prefix].append((ttl, mss, win))

return clusters

for prefix, fps in get_syn_fingerprints('eth0').items():

if len(fps) > 50:

ttls = set(f[0] for f in fps)

msss = set(f[1] for f in fps)

print(f"{prefix}/64: {len(fps)} sessions, TTL={ttls}, MSS={msss}")

```

Если вывод показывает `2000:abcd:1234:5678/64: 500 sessions, TTL={'47'}, MSS={'1460'}` — это прокси-пул без подмены TTL.

Что делать: чеклист для прокси-сервиса

Первое — подмена TTL по сессии. Не глобально, а с картой OS → TTL. Карту строить по User-Agent первого HTTP-запроса или по TCP-опциям (Windows ставит timestamp с определённым паттерном).

Второе — MSS clamping только там, где нужно. Не фиксировать MSS для всех. Пробрасывать реальный MSS клиента, если MTU позволяет. Если туннель — clamping по MTU маршрута, а не по константе.

Третье — окно TCP. Linux и Windows имеют разные начальные окна. Если прокси на Linux форвардит Windows-клиента, окно в SYN-ACK будет Linux. Нужно подменять.

Четвёртое — IPv6-префиксы. Не использовать один /64 для сотен клиентов. Разбивать на /64, каждый — один клиент. Но это дорого: /48 = 65536 /64. Аренда /48 у RIR — от \$2000 в год.

Пятое — геолокация. IPv6-адрес должен быть физически в регионе клиента. Проверять через traceroute и RIPE Atlas.

Шестое — мониторинг. Раз в сутки прогонять свой пул через tcpdump-анализ и смотреть, не появилось ли кластеров. Антифрод обновляет базы, ваш fingerprint должен меняться.

Почему это гонка без финиша

Антифрод маркетплейса и прокси-сервис играют в кошки-мышки. Антифрод добавляет новый параметр в fingerprint — прокси-сервис учится его подменять. Антифрод добавляет корреляцию между параметрами — прокси-сервис строит карту соответствия. Антифрод добавляет машинное обучение на временных рядах — прокси-сервис начинает эмулировать поведение.

На уровне TTL и MSS это уже пройденный этап для крупных сервисов. lexic.ml, работающий с 2015 года, видел эту эволюцию: в 2016 хватало подмены User-Agent, в 2019 — TTL, в 2022 — комбинации TTL+MSS+window, в 2024 — поведенческих паттернов. Каждый новый слой отсеивает часть пулов. Выживают те, кто эмулирует полный стек, а не отдельные поля.

Практический вывод: если вы строите прокси-сервис на IPv6, не экономьте на подмене L3/L4. Один /64 на клиента, карта OS → TTL/MSS/window, геолокация по traceroute, мониторинг fingerprint раз в сутки. Иначе антифрод маркетплейса отсеет пул за неделю-две, и вы даже не поймёте, почему.

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