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

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

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

Что вообще видит маркетплейс

Когда ваш парсер стучится на wildberries или ozon, сервер получает три независимых слоя информации. Первый — сетевой: IP, порт, ASN, геолокация. Второй — транспортный: параметры TCP-соединения, порядок опций, window size, TTL. Третий — TLS-рукопожатие и HTTP-заголовки. Антифрод склеивает всё это в один отпечаток и сравнивает с миллионами других.

IPv6 тут ничего не меняет на уровне TLS. ClientHello приходит с тем же набором cipher suites, extensions и elliptic curves, что и через IPv4. Разница только в том, что адресное пространство IPv6 огромное, и блокировка по подсети /64 не работает так, как блокировка /24 в IPv4. Это и создаёт иллюзию анонимности. Антифрод-вендоры давно научились ловить не по IP, а по TLS-отпечатку.

JA3 и JA3N — как считается

JA3 — это MD5 от строки, собранной из пяти полей ClientHello. Salesforce открыла спецификацию в 2017 году, и с тех пор она стала стандартом де-факто.

```

SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat

```

Числа берутся из реестра IANA. Например, TLS 1.3 = 772, cipher 4865 = TLS_AES_128_GCM_SHA256, extension 43 = supported_versions. Строка для Chrome 120 через IPv6 выглядит примерно так:

```

771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21,29-23-24,0

```

MD5 от неё — это и есть JA3. Проблема в том, что Chrome рандомизирует порядок extensions в ClientHello с версии 110. Поэтому появился JA3N — та же строка, но с отсортированными по возрастанию полями. Стабильнее.

Антифрод маркетплейса хранит базу JA3-хешей. Если ваш прокси-пул выдаёт один и тот же JA3 для тысяч сессий с разных IPv6 — это красный флаг. Реальные пользователи приходят с десятков разных отпечатков: разные версии Chrome, Firefox, Safari, мобильные клиенты.

Почему IPv6 прокси палятся сильнее IPv4

Парадокс, но IPv6-пулы часто детектятся быстрее. Причина в структуре адресации. Провайдер выдаёт клиенту префикс /56 или /64. Если прокси-сервис арендует один такой префикс и раздаёт адреса из него, все они попадают в одну подсеть. Антифрод видит: 5000 запросов с 5000 разных адресов, но все из одного /64, с одинаковым JA3, одинаковым порядком HTTP/2-заголовков, одинаковым User-Agent. Это не 5000 пользователей, это один бот.

В IPv4 та же логика работала через /24. Но IPv4-адресов мало, и антифрод привык к тому, что один /24 может быть корпоративной сетью или NAT-шлюзом. IPv6-подсети для residential-трафика обычно означают жилой сектор, и массовые запросы оттуда с идентичным TLS — аномалия.

HTTP/2 fingerprint — второй слой

TLS — не единственное. Akamai придумала схему, где отпечаток строится из HTTP/2-фреймов: SETTINGS, WINDOW_UPDATE, PRIORITY, HEADERS. Каждый клиент отправляет их в определённом порядке с определёнными значениями.

Chrome отправляет SETTINGS с параметрами `HEADER_TABLE_SIZE=65536`, `MAX_CONCURRENT_STREAMS=1000`, `INITIAL_WINDOW_SIZE=6291456`, `MAX_HEADER_LIST_SIZE=262144`. Firefox — другие значения. Python requests с httpx — третьи. Если ваш парсер использует requests поверх IPv6-прокси, HTTP/2-отпечаток будет отличаться от браузерного, даже если JA3 вы подделали.

Собрать оба отпечатка можно через `curl_cffi` или `tls-client`. Вот как выглядит проверка через Python:

```python

from curl_cffi import requests

session = requests.Session(impersonate="chrome120")

r = session.get(

"https://api.ipify.org?format=json",

proxies={"https": "http://[2a01:4f8:c17:1234::1]:8080"},

timeout=15,

)

print(r.json())

```

`impersonate` подменяет и JA3, и HTTP/2-фреймы, и порядок заголовков. Без него антифрод склеит вас за один запрос.

TCP/IP fingerprint и p0f

Пока TLS-рукопожатие не началось, сервер уже получил SYN-пакет. По нему можно построить отпечаток ОС: TTL, размер окна, набор TCP-опций (MSS, SACK, Timestamps, Window Scale), порядок их следования. Инструмент p0f делает это с 2012 года, и его сигнатуры встроены в большинство WAF.

Прокси-сервер на Linux с дефолтными настройками ядра отдаёт TTL 64, window 29200, опции в порядке MSS, SACK_PERM, TS, NOP, WS. Windows 10 — TTL 128, window 64240, другой порядок. Если вы эмулируете Chrome на Windows, но TCP-отпечаток у вас линуксовый — это несоответствие. Антифрод такие пары ловит таблицей соответствий.

IPv6 тут добавляет деталь: hop limit в IPv6-заголовке аналог TTL, и у прокси он тоже 64. Но настоящий residential-клиент за NAT может отдавать 63 или 62 из-за промежуточного роутера. Идентичный hop limit 64 у всех запросов — признак серверного пула.

Что делает антифрод маркетплейса на практике

Типичная схема на стороне Ozon или WB выглядит так. На входе — nginx или Envoy, который логирует TLS-параметры через модуль `ssl_preread` или Lua-хук. Дальше поток идёт в Kafka, оттуда в потоковый обработчик на Flink или Spark Streaming. Там считаются агрегаты: сколько уникальных JA3 на подсеть, сколько запросов на отпечаток в минуту, как распределены интервалы между запросами.

Дальше — правила. Если с одной /64-подсети больше 100 запросов в минуту с одинаковым JA3N — бан подсети. Если интервалы между запросами слишком ровные (стандартное отклонение меньше 50 мс) — это скрипт, не человек. Если User-Agent говорит Chrome 120, а JA3 соответствует Chrome 118 — несоответствие версий.

Вот пример конфига nginx, который логирует JA3 через Lua:

```nginx

lua_shared_dict ja3_cache 10m;

server {

listen [::]:443 ssl;

ssl_certificate /etc/ssl/cert.pem;

ssl_certificate_key /etc/ssl/key.pem;

ssl_preread on;

preread_by_lua_block {

local sock = ngx.req.socket(true)

local data, err = sock:peek(4096)

if data then

local ja3 = require("ja3").calculate(data)

ngx.var.ja3_hash = ja3

end

}

access_log /var/log/nginx/access.log combined_with_ja3;

}

```

Модуль `ja3` парсит ClientHello из preread-буфера и считает MD5. Дальше хеш уходит в лог и в аналитику.

Реальный пример: пул из 2000 IPv6 и один JA3

Пример: сервер на nginx 1.24 с ssl_preread и Lua-хуком, пул прокси из 2000 IPv6-адресов в одной /64, парсер на Python с requests. Через 40 минут после старта антифрод маркетплейса начал отдавать 403 на 80% запросов. Логи показали: все 2000 адресов дали один JA3 `a0e9f5d64349fb13191bc781f81f42e1`, что соответствует Python requests 2.31. Реальные браузеры дают минимум 15 разных хешей на тот же период.

Причина: requests использует OpenSSL с дефолтным набором cipher suites, порядок которых не совпадает ни с одним браузером. Плюс HTTP/2 не поддерживается вообще, только HTTP/1.1. Решение: переход на `curl_cffi` с `impersonate="chrome120"` и распределение адресов по разным /64-префиксам. После этого 403 упали до 3%.

Подсеть /64 — главный маркер

Антифрод считает не только уникальные IP, но и уникальные префиксы. Если 5000 адресов принадлежат одной /64, это одна точка присутствия. Residential-провайдеры раздают /56 или /48 на домохозяйство, и там адреса меняются редко. Дата-центры (Hetzner, OVH, DigitalOcean) выдают /64 на VPS, и вся подсеть принадлежит одной машине.

Проверить принадлежность можно через whois или RDAP:

```bash

curl -s "https://rdap.db.ripe.net/ip/2a01:4f8:c17::1" | jq '.entities[].vcardArray[1][] | select(.[0]=="fn")'

```

Если в ответе `Hetzner Online GmbH` — это дата-центр, и антифрод это знает. Residential-прокси через IPv6 почти не существует в чистом виде: мобильные операторы выдают IPv6 клиентам, но не продают доступ к ним. Поэтому IPv6-пулы в 90% случаев — это VPS, и антифрод это учитывает в скоринге.

Как выглядит настоящий residential IPv6

Мобильный оператор в Германии или США выдаёт клиенту IPv6-префикс /64, который меняется при переподключении. Трафик идёт через CGNAT для IPv4, но IPv6 маршрутизируется напрямую. Если ваш прокси-сервис заявляет residential IPv6, проверьте ASN: он должен принадлежать Deutsche Telekom, Vodafone, Verizon, T-Mobile, а не Hetzner или Leaseweb.

Вот проверка ASN через API:

```python

import httpx

async def check_asn(ip: str) -> dict:

async with httpx.AsyncClient() as client:

r = await client.get(f"https://api.bgpview.io/ip/{ip}")

data = r.json()["data"]

return {

"asn": data["prefixes"][0]["asn"]["asn"],

"name": data["prefixes"][0]["asn"]["name"],

"description": data["prefixes"][0]["asn"]["description"],

}

```

Если `name` содержит `Hetzner`, `OVH`, `DigitalOcean`, `Amazon` — это хостинг, не residential. Антифрод маркетплейсов держит такие списки и добавляет +30-50 к скорингу риска.

Склейка отпечатков и что с этим делать

Антифрод не смотрит на один параметр. Он строит вектор: JA3, JA3N, HTTP/2 fingerprint, TCP fingerprint, ASN, префикс /64, интервалы между запросами, набор запрашиваемых URL, порядок заголовков. Каждый параметр даёт вклад в итоговый скор. Если сумма превышает порог — бан.

Обойти это можно только полной эмуляцией браузера на всех уровнях. `curl_cffi` закрывает TLS и HTTP/2. TCP-отпечаток надо подгонять через настройки ядра или через прокси, который умеет его эмулировать. IPv6-адреса надо брать из residential-ASN и распределять по разным /64. Интервалы между запросами — рандомизировать с человеческим распределением, не равномерным.

Вот пример настройки sysctl для эмуляции Windows-клиента на Linux-прокси:

```bash

sysctl -w net.ipv4.tcp_window_scaling=1

sysctl -w net.ipv4.tcp_timestamps=1

sysctl -w net.ipv4.tcp_sack=1

sysctl -w net.ipv4.tcp_syncookies=0

```

Это не даст полного совпадения с Windows, но уберёт самые грубые несоответствия. Полная эмуляция TCP-стека требует userspace-стека вроде gVisor или lwIP, и это уже другая история.

Почему IPv6-прокси всё ещё работают

Несмотря на все слои детекта, IPv6-прокси остаются рабочим инструментом. Причина простая: маркетплейсы не могут банить всё агрессивно, иначе пострадают реальные пользователи с мобильных операторов. Порог скоринга подбирается так, чтобы отсекать грубые атаки, но пропускать пограничные случаи. Если ваш парсер выглядит как 70% браузера и 30% бота — вас пропустят.

Плюс IPv6 даёт то, чего нет в IPv4: огромное адресное пространство. Даже если антифрод забанит одну /64, у вас есть миллионы других. Ротация по префиксам работает быстрее, чем ротация по отдельным IPv4-адресам, которых у прокси-сервиса может быть всего 10 тысяч.

Сервисы вроде lexic.ml дают пулы IPv6 с ротацией по /64-префиксам, что снижает риск бана подсети. Но сам по себе IPv6 не решает проблему TLS-отпечатка. Без эмуляции браузера на уровне TLS и HTTP/2 любой прокси палится за минуты.

Итоговая картина

Антифрод маркетплейса — это многослойная система. TLS-отпечаток ловит несоответствие клиента. HTTP/2-фреймы добивают тех, кто подделал только TLS. TCP-отпечаток отсекает серверные пулы. ASN и префикс /64 выявляют дата-центры. Интервалы и паттерны запросов ловят скрипты. Обойти всё это можно только комплексной эмуляцией, и IPv6 тут — лишь один из слоёв, не панацея.

Рабочая схема выглядит так: residential IPv6 из правильного ASN, `curl_cffi` с `impersonate`, рандомизация интервалов, ротация по /64, разные User-Agent и JA3 для разных сессий. Без любого из этих элементов антифрод вас поймает. С ними — шансы выжить в течение недель, а не часов.

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