Как антифрод-системы маркетплейсов отличают IPv6-датацентр от residential по TTL и хопам
Содержание
- Почему IPv6 сломал старую логику TTL
- Что реально считает антифрод
- Восстановление начального TTL
- TCP fingerprint как добивка
- RTT и географическая привязка
- Кейс: парсинг цен через /64 из OVH
- Кейс: подмена TTL и что пошло не так
- MTU как дополнительный сигнал
- Privacy Extensions и суффикс адреса
- низкие значения суффикса — ручная настройка
- Что делать, если нужен реальный residential
- Итог
Антифрод маркетплейсов давно перестал смотреть только на IP-адрес. Чёрные списки подсетей устарели лет пять назад: провайдеры продают /64 из residential-пулов, а датацентры арендуют адреса у тех же RIR. Поэтому в ход пошли метрики сетевого уровня — TTL, hop count, TCP fingerprint, RTT. По ним видно, идёт ли трафик с домашнего роутера или с виртуалки в Hetzner.
Разберём, как это работает на IPv6, чем отличается от IPv4 и почему один TTL уже ничего не решает.
Почему IPv6 сломал старую логику TTL
В IPv4 TTL — это счётчик, который уменьшается на каждом маршрутизаторе. Классическая эвристика: если TTL=64, скорее всего Linux с дефолтом, если 128 — Windows, если 255 — сеть. Антифрод вычитал начальный TTL из наблюдаемого и получал число хопов. Меньше 5 хопов — почти наверняка датацентр рядом с целевым сервером.
IPv6 формально использует Hop Limit — то же поле, но с другим именем. Значение по умолчанию у Linux — 64, у Windows — 128. Разница в том, что IPv6-маршрутизация часто короче: крупные IX вроде DE-CIX и AMS-IX анонсируют префиксы напрямую, и путь от residential-абонента до сервера маркетплейса может быть 8-12 хопов, тогда как от датацентра — 3-5. Именно эта разница и стала главным сигналом.
Но тут начинается зоопарк. Мобильные операторы вроде МТС или Vodafone часто ставят CGNAT с Hop Limit=63 или 62 на выходе. Некоторые CDN-провайдеры переписывают поле. Cloudflare, например, исторически выставляет Hop Limit=63 на исходящих. Так что один только TTL даёт ложные срабатывания в обе стороны.
Что реально считает антифрод
Маркетплейсы (Avito, Ozon, Wildberries, eBay) собирают десятки сигналов одновременно. По сети — это:
- Hop count до клиента (через ICMPv6 Time Exceeded или TCP-анализ)
- Начальный TTL, восстановленный из наблюдаемого
- TCP/IP fingerprint (p0f, JA3/JA4, TCP timestamps)
- RTT первого пакета (SYN → SYN-ACK)
- MTU Path Discovery — датацентры часто отдают 1500, residential с PPPoE — 1492
- Наличие IPv6-фрагментации, размер окна, опции TCP
Отдельно смотрят на IPv6-специфику: extension headers, flow label, наличие Privacy Extensions (RFC 4941). Residential-устройства почти всегда используют временные адреса с рандомизированным интерфейсным идентификатором. Серверы в датацентрах — стабильные EUI-64 или фиксированные суффиксы.
Восстановление начального TTL
Практический приём: сервер маркетплейса видит пакет с Hop Limit=54. Возможные начальные значения — 64, 128, 255. Ближайшее сверху — 64, значит хопов было 10. Если видит 60 — хопов 4, начальный 64. Это уже подозрительно.
Проверить можно простым скриптом через scapy или обычным ping6 с трассировкой:
```bash
traceroute6 -n -q 1 -w 1 2a03:2880:f10c:83:face:b00c:0:25de
```
На Linux можно вытащить TTL из ответа:
```bash
ping6 -c 1 -W 1 google.com | grep -oP 'hlim=\K\d+'
```
Python-вариант с восстановлением начального значения:
```python
import subprocess, re
def probe_hop_limit(host):
out = subprocess.run(
["ping6", "-c", "1", "-W", "1", host],
capture_output=True, text=True
).stdout
m = re.search(r"hlim=(\d+)", out)
if not m:
return None
observed = int(m.group(1))
for initial in (64, 128, 255):
if observed <= initial:
return initial, initial - observed
return None, None
print(probe_hop_limit("2606:4700:4700::1111"))
```
Для residential-подключения в Москве до сервера в Франкфурте типично 12-15 хопов. От виртуалки в том же Франкфурте — 3-4.
TCP fingerprint как добивка
TTL легко подделать через iptables или sysctl. А вот TCP-стек — сложнее. p0f смотрит на:
- Window size (у Linux 2.6+ — 29200, у Windows 10 — 65535)
- TCP options: MSS, SACK permitted, Timestamps, NOP, Window Scale
- Порядок опций — уникален для каждой ОС и версии ядра
Датацентр-прокси на Debian 12 с ядром 6.1 выдаст один fingerprint. Реальный iPhone через Wi-Fi — совсем другой. Антифрод маркетплейса сравнивает fingerprint с заявленным User-Agent. Если UA говорит "iPhone Safari", а TCP-стек — Linux 6.x, это красный флаг.
Вот как выглядит сбор сигналов в Python через p0f-подобный анализ на стороне сервера:
```python
from scapy.all import sniff, TCP, IP, IPv6
def on_packet(pkt):
if IPv6 in pkt and TCP in pkt:
tcp = pkt[TCP]
opts = [o[0] for o in tcp.options]
print({
"src": pkt[IPv6].src,
"hop_limit": pkt[IPv6].hlim,
"window": tcp.window,
"options": opts,
"mss": dict(tcp.options).get("MSS"),
})
sniff(filter="tcp and ip6", prn=on_packet, count=20)
```
По набору опций и window size можно с высокой точностью отделить Linux-сервер от мобильного клиента.
RTT и географическая привязка
Маркетплейсы знают примерную топологию интернета. Если RTT до клиента 2 мс, а заявленный адрес — residential в Новосибирске, физика не сходится: свет по оптоволокну идёт 5 мкс на километр, минимум 30 мс до Москвы. Значит, клиент сидит в датацентре рядом с сервером.
Типичные цифры:
| Сценарий | RTT | Hop count | Начальный TTL |
|---|---|---|---|
| Residential Москва → сервер во Франкфурте | 35-45 мс | 12-16 | 64 |
| VPS Hetzner → сервер во Франкфурте | 1-3 мс | 2-4 | 64 |
| Мобильный LTE → сервер в Москве | 40-70 мс | 10-14 | 63 |
| Cloudflare Worker → origin | 0.5-2 мс | 1-2 | 63 |
Комбинация RTT < 5 мс + hop count < 5 + стабильный IPv6-суффикс = датацентр с вероятностью 95%.
Кейс: парсинг цен через /64 из OVH
Команда занималась мониторингом цен на маркетплейсе электроники. Арендовали /64 в OVH, разворачивали 5000 адресов, каждый запрос — с нового. Первые сутки работало. На вторые — капча на 80% запросов.
Причина: OVH анонсирует свой /64 через один пограничный маршрутизатор. Hop count до сервера маркетплейса — 4. TTL во всех запросах — 60, начальный 64. RTT стабильно 3-5 мс. Антифрод увидел 5000 разных IPv6, но все с одинаковым fingerprint, одинаковым hop count и RTT в узком диапазоне 3±1 мс. Это подпись датацентра.
Решение — не в подмене TTL (это лечится за час), а в распределении по residential-прокси. Для реальных задач сбора данных с маркетплейсов нужны residential-пулы с настоящими домашними подключениями, а не арендованные /64. Сервисы вроде lexic.ml отдают IPv6 из residential-диапазонов, где hop count и RTT совпадают с реальными абонентами конкретного региона.
Кейс: подмена TTL и что пошло не так
Инженер решил обойти детект через iptables:
```bash
ip6tables -t mangle -A POSTROUTING -j HL --hl-set 64
```
Все исходящие пакеты получили Hop Limit=64. Логика: сервер увидит 64, решит, что клиент в одном хопе. Но это работает наоборот. Сервер в датацентре получает пакет с HL=64, вычитает свой начальный 64 — выходит 0 хопов. Такого не бывает: даже localhost имеет минимум один интерфейс. Антифрод помечает трафик как аномальный.
Правильная подмена — выставить HL так, чтобы после всех маршрутизаторов получилось правдоподобное значение. Например, если до сервера 4 хопа, ставить 64+4=68. Но тогда ломается обратный путь и ICMPv6 Path MTU Discovery начинает сыпать ошибки. Плюс p0f всё равно видит Linux-ядро, а не macOS.
MTU как дополнительный сигнал
IPv6 не фрагментирует пакеты на маршрутизаторах — только источник. Минимальный MTU для IPv6 — 1280 байт. Residential с PPPoE обычно имеет MTU 1492, с DHCPv6 — 1500. Датацентры почти всегда 1500.
Если клиент заявляет себя как домашний пользователь в Германии, а его PMTU = 1500 без единой фрагментации — это не PPPoE. Значит, либо кабель, либо датацентр. Антифрод делает активную проверку: отправляет пакет 1500 байт с DF и смотрит на ICMPv6 Packet Too Big. Residential-провайдеры часто отдают MTU 1480-1492, датацентры — 1500 ровно.
Privacy Extensions и суффикс адреса
RFC 4941 предписывает residential-устройствам использовать временные адреса. Суффикс меняется каждые несколько часов, интерфейсный идентификатор выглядит случайным. Windows и macOS это делают по умолчанию. Linux — только если включён `net.ipv6.conf.all.use_tempaddr=2`.
Серверы в датацентрах обычно используют статический суффикс: EUI-64 из MAC или фиксированный адрес. Если из /64 приходят 5000 адресов с суффиксами, отличающимися только в последних 16 битах — это один хост с несколькими адресами, а не 5000 клиентов.
Проверка на сервере:
```python
def is_suspicious_suffix(addr: str) -> bool:
suffix = addr.split(":")[-4:]
as_int = int("".join(suffix), 16)
низкие значения суффикса — ручная настройка
return as_int < 0x100000000
```
Что делать, если нужен реальный residential
Три подхода, каждый со своими граблями.
Первый — покупать доступ к residential-прокси-сетям, где трафик идёт через реальные устройства абонентов. Дорого, но hop count и TTL совпадают с настоящими. Провайдеры вроде Bright Data или Smartproxy держат пулы, но маркетплейсы их тоже знают и банят целыми ASN.
Второй — мобильные прокси. LTE-операторы выдают IPv6 с CGNAT, hop count 10-14, RTT 40-70 мс, Privacy Extensions включены. Это самый чистый вариант по сетевым метрикам, но дорогой и с ротацией раз в 5-15 минут.
Третий — распределённые residential-пулы, где один IPv6 принадлежит реальному абоненту и не переиспользуется сотнями клиентов. Тут важна не только сеть, но и репутация: если адрес уже светился в парсинге, его забанят по поведенческим метрикам, а не по TTL.
Итог
Антифрод маркетплейса не смотрит на один сигнал. TTL и hop count — вход, но решение принимается по совокупности: TCP fingerprint, RTT, MTU, поведение суффикса, история адреса. Подделать один параметр легко, подделать все сразу — почти невозможно без реального residential-подключения.
Если задача — обойти детект, начинать надо не с iptables, а с вопроса: соответствует ли вся сетевая картина заявленному региону и типу подключения. Один неверный параметр из десяти обычно не палит. Три-четыре — палят гарантированно.